/* Copyright (c) 2009, Markus Peloquin <markus@cs.wisc.edu>
 * 
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */

/* I changed so much damn stuff in here that I feel I can copyright it.  I had
 * nothing to do with the c_# tables or the process_buffer algorithm. */

/**
 * The Whirlpool hashing function.
 *
 * <P>
 * <b>References</b>
 *
 * <P>
 * The Whirlpool algorithm was developed by
 * <a href="mailto:pbarreto@scopus.com.br">Paulo S. L. M. Barreto</a> and
 * <a href="mailto:vincent.rijmen@cryptomathic.com">Vincent Rijmen</a>.
 *
 * See
 *      P.S.L.M. Barreto, V. Rijmen,
 *      ``The Whirlpool hashing function,''
 *      NESSIE submission, 2000 (tweaked version, 2001),
 *      <https://www.cosic.esat.kuleuven.ac.be/nessie/workshop/submissions/whirlpool.zip>
 * 
 * @author  Paulo S.L.M. Barreto
 * @author  Vincent Rijmen.
 *
 * @version 3.0 (2003.03.12)
 *
 * =============================================================================
 *
 * Differences from version 2.1:
 *
 * - Suboptimal diffusion matrix replaced by cir(1, 1, 4, 1, 8, 5, 2, 9).
 *
 * =============================================================================
 *
 * Differences from version 2.0:
 *
 * - Generation of ISO/IEC 10118-3 test vectors.
 * - Bug fix: nonzero carry was ignored when tallying the data length
 *      (this bug apparently only manifested itself when feeding data
 *      in pieces rather than in a single chunk at once).
 * - Support for MS Visual C++ 64-bit integer arithmetic.
 *
 * Differences from version 1.0:
 *
 * - Original S-box replaced by the tweaked, hardware-efficient version.
 *
 * =============================================================================
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#include <assert.h>
#include <string.h>

#include "endian.h"
#include "whirlpool.h"

#define DIGESTBYTES 64
#define WBLOCKBYTES 64
#define LENGTHBYTES 32

/* the number of rounds of the internal dedicated block cipher */
#define ROUNDS 10

/* Though Whirlpool is endianness-neutral, the encryption tables are listed
 * in BIG-ENDIAN format, which is adopted throughout this implementation
 * (but little-endian notation would be equally suitable if consistently
 * employed). */

static const uint64_t c_0[0x100] = {
    0x18186018c07830d8LL, 0x23238c2305af4626LL, 0xc6c63fc67ef991b8LL, 0xe8e887e8136fcdfbLL,
    0x878726874ca113cbLL, 0xb8b8dab8a9626d11LL, 0x0101040108050209LL, 0x4f4f214f426e9e0dLL,
    0x3636d836adee6c9bLL, 0xa6a6a2a6590451ffLL, 0xd2d26fd2debdb90cLL, 0xf5f5f3f5fb06f70eLL,
    0x7979f979ef80f296LL, 0x6f6fa16f5fcede30LL, 0x91917e91fcef3f6dLL, 0x52525552aa07a4f8LL,
    0x60609d6027fdc047LL, 0xbcbccabc89766535LL, 0x9b9b569baccd2b37LL, 0x8e8e028e048c018aLL,
    0xa3a3b6a371155bd2LL, 0x0c0c300c603c186cLL, 0x7b7bf17bff8af684LL, 0x3535d435b5e16a80LL,
    0x1d1d741de8693af5LL, 0xe0e0a7e05347ddb3LL, 0xd7d77bd7f6acb321LL, 0xc2c22fc25eed999cLL,
    0x2e2eb82e6d965c43LL, 0x4b4b314b627a9629LL, 0xfefedffea321e15dLL, 0x575741578216aed5LL,
    0x15155415a8412abdLL, 0x7777c1779fb6eee8LL, 0x3737dc37a5eb6e92LL, 0xe5e5b3e57b56d79eLL,
    0x9f9f469f8cd92313LL, 0xf0f0e7f0d317fd23LL, 0x4a4a354a6a7f9420LL, 0xdada4fda9e95a944LL,
    0x58587d58fa25b0a2LL, 0xc9c903c906ca8fcfLL, 0x2929a429558d527cLL, 0x0a0a280a5022145aLL,
    0xb1b1feb1e14f7f50LL, 0xa0a0baa0691a5dc9LL, 0x6b6bb16b7fdad614LL, 0x85852e855cab17d9LL,
    0xbdbdcebd8173673cLL, 0x5d5d695dd234ba8fLL, 0x1010401080502090LL, 0xf4f4f7f4f303f507LL,
    0xcbcb0bcb16c08bddLL, 0x3e3ef83eedc67cd3LL, 0x0505140528110a2dLL, 0x676781671fe6ce78LL,
    0xe4e4b7e47353d597LL, 0x27279c2725bb4e02LL, 0x4141194132588273LL, 0x8b8b168b2c9d0ba7LL,
    0xa7a7a6a7510153f6LL, 0x7d7de97dcf94fab2LL, 0x95956e95dcfb3749LL, 0xd8d847d88e9fad56LL,
    0xfbfbcbfb8b30eb70LL, 0xeeee9fee2371c1cdLL, 0x7c7ced7cc791f8bbLL, 0x6666856617e3cc71LL,
    0xdddd53dda68ea77bLL, 0x17175c17b84b2eafLL, 0x4747014702468e45LL, 0x9e9e429e84dc211aLL,
    0xcaca0fca1ec589d4LL, 0x2d2db42d75995a58LL, 0xbfbfc6bf9179632eLL, 0x07071c07381b0e3fLL,
    0xadad8ead012347acLL, 0x5a5a755aea2fb4b0LL, 0x838336836cb51befLL, 0x3333cc3385ff66b6LL,
    0x636391633ff2c65cLL, 0x02020802100a0412LL, 0xaaaa92aa39384993LL, 0x7171d971afa8e2deLL,
    0xc8c807c80ecf8dc6LL, 0x19196419c87d32d1LL, 0x494939497270923bLL, 0xd9d943d9869aaf5fLL,
    0xf2f2eff2c31df931LL, 0xe3e3abe34b48dba8LL, 0x5b5b715be22ab6b9LL, 0x88881a8834920dbcLL,
    0x9a9a529aa4c8293eLL, 0x262698262dbe4c0bLL, 0x3232c8328dfa64bfLL, 0xb0b0fab0e94a7d59LL,
    0xe9e983e91b6acff2LL, 0x0f0f3c0f78331e77LL, 0xd5d573d5e6a6b733LL, 0x80803a8074ba1df4LL,
    0xbebec2be997c6127LL, 0xcdcd13cd26de87ebLL, 0x3434d034bde46889LL, 0x48483d487a759032LL,
    0xffffdbffab24e354LL, 0x7a7af57af78ff48dLL, 0x90907a90f4ea3d64LL, 0x5f5f615fc23ebe9dLL,
    0x202080201da0403dLL, 0x6868bd6867d5d00fLL, 0x1a1a681ad07234caLL, 0xaeae82ae192c41b7LL,
    0xb4b4eab4c95e757dLL, 0x54544d549a19a8ceLL, 0x93937693ece53b7fLL, 0x222288220daa442fLL,
    0x64648d6407e9c863LL, 0xf1f1e3f1db12ff2aLL, 0x7373d173bfa2e6ccLL, 0x12124812905a2482LL,
    0x40401d403a5d807aLL, 0x0808200840281048LL, 0xc3c32bc356e89b95LL, 0xecec97ec337bc5dfLL,
    0xdbdb4bdb9690ab4dLL, 0xa1a1bea1611f5fc0LL, 0x8d8d0e8d1c830791LL, 0x3d3df43df5c97ac8LL,
    0x97976697ccf1335bLL, 0x0000000000000000LL, 0xcfcf1bcf36d483f9LL, 0x2b2bac2b4587566eLL,
    0x7676c57697b3ece1LL, 0x8282328264b019e6LL, 0xd6d67fd6fea9b128LL, 0x1b1b6c1bd87736c3LL,
    0xb5b5eeb5c15b7774LL, 0xafaf86af112943beLL, 0x6a6ab56a77dfd41dLL, 0x50505d50ba0da0eaLL,
    0x45450945124c8a57LL, 0xf3f3ebf3cb18fb38LL, 0x3030c0309df060adLL, 0xefef9bef2b74c3c4LL,
    0x3f3ffc3fe5c37edaLL, 0x55554955921caac7LL, 0xa2a2b2a2791059dbLL, 0xeaea8fea0365c9e9LL,
    0x656589650fecca6aLL, 0xbabad2bab9686903LL, 0x2f2fbc2f65935e4aLL, 0xc0c027c04ee79d8eLL,
    0xdede5fdebe81a160LL, 0x1c1c701ce06c38fcLL, 0xfdfdd3fdbb2ee746LL, 0x4d4d294d52649a1fLL,
    0x92927292e4e03976LL, 0x7575c9758fbceafaLL, 0x06061806301e0c36LL, 0x8a8a128a249809aeLL,
    0xb2b2f2b2f940794bLL, 0xe6e6bfe66359d185LL, 0x0e0e380e70361c7eLL, 0x1f1f7c1ff8633ee7LL,
    0x6262956237f7c455LL, 0xd4d477d4eea3b53aLL, 0xa8a89aa829324d81LL, 0x96966296c4f43152LL,
    0xf9f9c3f99b3aef62LL, 0xc5c533c566f697a3LL, 0x2525942535b14a10LL, 0x59597959f220b2abLL,
    0x84842a8454ae15d0LL, 0x7272d572b7a7e4c5LL, 0x3939e439d5dd72ecLL, 0x4c4c2d4c5a619816LL,
    0x5e5e655eca3bbc94LL, 0x7878fd78e785f09fLL, 0x3838e038ddd870e5LL, 0x8c8c0a8c14860598LL,
    0xd1d163d1c6b2bf17LL, 0xa5a5aea5410b57e4LL, 0xe2e2afe2434dd9a1LL, 0x616199612ff8c24eLL,
    0xb3b3f6b3f1457b42LL, 0x2121842115a54234LL, 0x9c9c4a9c94d62508LL, 0x1e1e781ef0663ceeLL,
    0x4343114322528661LL, 0xc7c73bc776fc93b1LL, 0xfcfcd7fcb32be54fLL, 0x0404100420140824LL,
    0x51515951b208a2e3LL, 0x99995e99bcc72f25LL, 0x6d6da96d4fc4da22LL, 0x0d0d340d68391a65LL,
    0xfafacffa8335e979LL, 0xdfdf5bdfb684a369LL, 0x7e7ee57ed79bfca9LL, 0x242490243db44819LL,
    0x3b3bec3bc5d776feLL, 0xabab96ab313d4b9aLL, 0xcece1fce3ed181f0LL, 0x1111441188552299LL,
    0x8f8f068f0c890383LL, 0x4e4e254e4a6b9c04LL, 0xb7b7e6b7d1517366LL, 0xebeb8beb0b60cbe0LL,
    0x3c3cf03cfdcc78c1LL, 0x81813e817cbf1ffdLL, 0x94946a94d4fe3540LL, 0xf7f7fbf7eb0cf31cLL,
    0xb9b9deb9a1676f18LL, 0x13134c13985f268bLL, 0x2c2cb02c7d9c5851LL, 0xd3d36bd3d6b8bb05LL,
    0xe7e7bbe76b5cd38cLL, 0x6e6ea56e57cbdc39LL, 0xc4c437c46ef395aaLL, 0x03030c03180f061bLL,
    0x565645568a13acdcLL, 0x44440d441a49885eLL, 0x7f7fe17fdf9efea0LL, 0xa9a99ea921374f88LL,
    0x2a2aa82a4d825467LL, 0xbbbbd6bbb16d6b0aLL, 0xc1c123c146e29f87LL, 0x53535153a202a6f1LL,
    0xdcdc57dcae8ba572LL, 0x0b0b2c0b58271653LL, 0x9d9d4e9d9cd32701LL, 0x6c6cad6c47c1d82bLL,
    0x3131c43195f562a4LL, 0x7474cd7487b9e8f3LL, 0xf6f6fff6e309f115LL, 0x464605460a438c4cLL,
    0xacac8aac092645a5LL, 0x89891e893c970fb5LL, 0x14145014a04428b4LL, 0xe1e1a3e15b42dfbaLL,
    0x16165816b04e2ca6LL, 0x3a3ae83acdd274f7LL, 0x6969b9696fd0d206LL, 0x09092409482d1241LL,
    0x7070dd70a7ade0d7LL, 0xb6b6e2b6d954716fLL, 0xd0d067d0ceb7bd1eLL, 0xeded93ed3b7ec7d6LL,
    0xcccc17cc2edb85e2LL, 0x424215422a578468LL, 0x98985a98b4c22d2cLL, 0xa4a4aaa4490e55edLL,
    0x2828a0285d885075LL, 0x5c5c6d5cda31b886LL, 0xf8f8c7f8933fed6bLL, 0x8686228644a411c2LL,
};

static const uint64_t c_1[0x100] = {
    0xd818186018c07830LL, 0x2623238c2305af46LL, 0xb8c6c63fc67ef991LL, 0xfbe8e887e8136fcdLL,
    0xcb878726874ca113LL, 0x11b8b8dab8a9626dLL, 0x0901010401080502LL, 0x0d4f4f214f426e9eLL,
    0x9b3636d836adee6cLL, 0xffa6a6a2a6590451LL, 0x0cd2d26fd2debdb9LL, 0x0ef5f5f3f5fb06f7LL,
    0x967979f979ef80f2LL, 0x306f6fa16f5fcedeLL, 0x6d91917e91fcef3fLL, 0xf852525552aa07a4LL,
    0x4760609d6027fdc0LL, 0x35bcbccabc897665LL, 0x379b9b569baccd2bLL, 0x8a8e8e028e048c01LL,
    0xd2a3a3b6a371155bLL, 0x6c0c0c300c603c18LL, 0x847b7bf17bff8af6LL, 0x803535d435b5e16aLL,
    0xf51d1d741de8693aLL, 0xb3e0e0a7e05347ddLL, 0x21d7d77bd7f6acb3LL, 0x9cc2c22fc25eed99LL,
    0x432e2eb82e6d965cLL, 0x294b4b314b627a96LL, 0x5dfefedffea321e1LL, 0xd5575741578216aeLL,
    0xbd15155415a8412aLL, 0xe87777c1779fb6eeLL, 0x923737dc37a5eb6eLL, 0x9ee5e5b3e57b56d7LL,
    0x139f9f469f8cd923LL, 0x23f0f0e7f0d317fdLL, 0x204a4a354a6a7f94LL, 0x44dada4fda9e95a9LL,
    0xa258587d58fa25b0LL, 0xcfc9c903c906ca8fLL, 0x7c2929a429558d52LL, 0x5a0a0a280a502214LL,
    0x50b1b1feb1e14f7fLL, 0xc9a0a0baa0691a5dLL, 0x146b6bb16b7fdad6LL, 0xd985852e855cab17LL,
    0x3cbdbdcebd817367LL, 0x8f5d5d695dd234baLL, 0x9010104010805020LL, 0x07f4f4f7f4f303f5LL,
    0xddcbcb0bcb16c08bLL, 0xd33e3ef83eedc67cLL, 0x2d0505140528110aLL, 0x78676781671fe6ceLL,
    0x97e4e4b7e47353d5LL, 0x0227279c2725bb4eLL, 0x7341411941325882LL, 0xa78b8b168b2c9d0bLL,
    0xf6a7a7a6a7510153LL, 0xb27d7de97dcf94faLL, 0x4995956e95dcfb37LL, 0x56d8d847d88e9fadLL,
    0x70fbfbcbfb8b30ebLL, 0xcdeeee9fee2371c1LL, 0xbb7c7ced7cc791f8LL, 0x716666856617e3ccLL,
    0x7bdddd53dda68ea7LL, 0xaf17175c17b84b2eLL, 0x454747014702468eLL, 0x1a9e9e429e84dc21LL,
    0xd4caca0fca1ec589LL, 0x582d2db42d75995aLL, 0x2ebfbfc6bf917963LL, 0x3f07071c07381b0eLL,
    0xacadad8ead012347LL, 0xb05a5a755aea2fb4LL, 0xef838336836cb51bLL, 0xb63333cc3385ff66LL,
    0x5c636391633ff2c6LL, 0x1202020802100a04LL, 0x93aaaa92aa393849LL, 0xde7171d971afa8e2LL,
    0xc6c8c807c80ecf8dLL, 0xd119196419c87d32LL, 0x3b49493949727092LL, 0x5fd9d943d9869aafLL,
    0x31f2f2eff2c31df9LL, 0xa8e3e3abe34b48dbLL, 0xb95b5b715be22ab6LL, 0xbc88881a8834920dLL,
    0x3e9a9a529aa4c829LL, 0x0b262698262dbe4cLL, 0xbf3232c8328dfa64LL, 0x59b0b0fab0e94a7dLL,
    0xf2e9e983e91b6acfLL, 0x770f0f3c0f78331eLL, 0x33d5d573d5e6a6b7LL, 0xf480803a8074ba1dLL,
    0x27bebec2be997c61LL, 0xebcdcd13cd26de87LL, 0x893434d034bde468LL, 0x3248483d487a7590LL,
    0x54ffffdbffab24e3LL, 0x8d7a7af57af78ff4LL, 0x6490907a90f4ea3dLL, 0x9d5f5f615fc23ebeLL,
    0x3d202080201da040LL, 0x0f6868bd6867d5d0LL, 0xca1a1a681ad07234LL, 0xb7aeae82ae192c41LL,
    0x7db4b4eab4c95e75LL, 0xce54544d549a19a8LL, 0x7f93937693ece53bLL, 0x2f222288220daa44LL,
    0x6364648d6407e9c8LL, 0x2af1f1e3f1db12ffLL, 0xcc7373d173bfa2e6LL, 0x8212124812905a24LL,
    0x7a40401d403a5d80LL, 0x4808082008402810LL, 0x95c3c32bc356e89bLL, 0xdfecec97ec337bc5LL,
    0x4ddbdb4bdb9690abLL, 0xc0a1a1bea1611f5fLL, 0x918d8d0e8d1c8307LL, 0xc83d3df43df5c97aLL,
    0x5b97976697ccf133LL, 0x0000000000000000LL, 0xf9cfcf1bcf36d483LL, 0x6e2b2bac2b458756LL,
    0xe17676c57697b3ecLL, 0xe68282328264b019LL, 0x28d6d67fd6fea9b1LL, 0xc31b1b6c1bd87736LL,
    0x74b5b5eeb5c15b77LL, 0xbeafaf86af112943LL, 0x1d6a6ab56a77dfd4LL, 0xea50505d50ba0da0LL,
    0x5745450945124c8aLL, 0x38f3f3ebf3cb18fbLL, 0xad3030c0309df060LL, 0xc4efef9bef2b74c3LL,
    0xda3f3ffc3fe5c37eLL, 0xc755554955921caaLL, 0xdba2a2b2a2791059LL, 0xe9eaea8fea0365c9LL,
    0x6a656589650feccaLL, 0x03babad2bab96869LL, 0x4a2f2fbc2f65935eLL, 0x8ec0c027c04ee79dLL,
    0x60dede5fdebe81a1LL, 0xfc1c1c701ce06c38LL, 0x46fdfdd3fdbb2ee7LL, 0x1f4d4d294d52649aLL,
    0x7692927292e4e039LL, 0xfa7575c9758fbceaLL, 0x3606061806301e0cLL, 0xae8a8a128a249809LL,
    0x4bb2b2f2b2f94079LL, 0x85e6e6bfe66359d1LL, 0x7e0e0e380e70361cLL, 0xe71f1f7c1ff8633eLL,
    0x556262956237f7c4LL, 0x3ad4d477d4eea3b5LL, 0x81a8a89aa829324dLL, 0x5296966296c4f431LL,
    0x62f9f9c3f99b3aefLL, 0xa3c5c533c566f697LL, 0x102525942535b14aLL, 0xab59597959f220b2LL,
    0xd084842a8454ae15LL, 0xc57272d572b7a7e4LL, 0xec3939e439d5dd72LL, 0x164c4c2d4c5a6198LL,
    0x945e5e655eca3bbcLL, 0x9f7878fd78e785f0LL, 0xe53838e038ddd870LL, 0x988c8c0a8c148605LL,
    0x17d1d163d1c6b2bfLL, 0xe4a5a5aea5410b57LL, 0xa1e2e2afe2434dd9LL, 0x4e616199612ff8c2LL,
    0x42b3b3f6b3f1457bLL, 0x342121842115a542LL, 0x089c9c4a9c94d625LL, 0xee1e1e781ef0663cLL,
    0x6143431143225286LL, 0xb1c7c73bc776fc93LL, 0x4ffcfcd7fcb32be5LL, 0x2404041004201408LL,
    0xe351515951b208a2LL, 0x2599995e99bcc72fLL, 0x226d6da96d4fc4daLL, 0x650d0d340d68391aLL,
    0x79fafacffa8335e9LL, 0x69dfdf5bdfb684a3LL, 0xa97e7ee57ed79bfcLL, 0x19242490243db448LL,
    0xfe3b3bec3bc5d776LL, 0x9aabab96ab313d4bLL, 0xf0cece1fce3ed181LL, 0x9911114411885522LL,
    0x838f8f068f0c8903LL, 0x044e4e254e4a6b9cLL, 0x66b7b7e6b7d15173LL, 0xe0ebeb8beb0b60cbLL,
    0xc13c3cf03cfdcc78LL, 0xfd81813e817cbf1fLL, 0x4094946a94d4fe35LL, 0x1cf7f7fbf7eb0cf3LL,
    0x18b9b9deb9a1676fLL, 0x8b13134c13985f26LL, 0x512c2cb02c7d9c58LL, 0x05d3d36bd3d6b8bbLL,
    0x8ce7e7bbe76b5cd3LL, 0x396e6ea56e57cbdcLL, 0xaac4c437c46ef395LL, 0x1b03030c03180f06LL,
    0xdc565645568a13acLL, 0x5e44440d441a4988LL, 0xa07f7fe17fdf9efeLL, 0x88a9a99ea921374fLL,
    0x672a2aa82a4d8254LL, 0x0abbbbd6bbb16d6bLL, 0x87c1c123c146e29fLL, 0xf153535153a202a6LL,
    0x72dcdc57dcae8ba5LL, 0x530b0b2c0b582716LL, 0x019d9d4e9d9cd327LL, 0x2b6c6cad6c47c1d8LL,
    0xa43131c43195f562LL, 0xf37474cd7487b9e8LL, 0x15f6f6fff6e309f1LL, 0x4c464605460a438cLL,
    0xa5acac8aac092645LL, 0xb589891e893c970fLL, 0xb414145014a04428LL, 0xbae1e1a3e15b42dfLL,
    0xa616165816b04e2cLL, 0xf73a3ae83acdd274LL, 0x066969b9696fd0d2LL, 0x4109092409482d12LL,
    0xd77070dd70a7ade0LL, 0x6fb6b6e2b6d95471LL, 0x1ed0d067d0ceb7bdLL, 0xd6eded93ed3b7ec7LL,
    0xe2cccc17cc2edb85LL, 0x68424215422a5784LL, 0x2c98985a98b4c22dLL, 0xeda4a4aaa4490e55LL,
    0x752828a0285d8850LL, 0x865c5c6d5cda31b8LL, 0x6bf8f8c7f8933fedLL, 0xc28686228644a411LL,
};

static const uint64_t c_2[0x100] = {
    0x30d818186018c078LL, 0x462623238c2305afLL, 0x91b8c6c63fc67ef9LL, 0xcdfbe8e887e8136fLL,
    0x13cb878726874ca1LL, 0x6d11b8b8dab8a962LL, 0x0209010104010805LL, 0x9e0d4f4f214f426eLL,
    0x6c9b3636d836adeeLL, 0x51ffa6a6a2a65904LL, 0xb90cd2d26fd2debdLL, 0xf70ef5f5f3f5fb06LL,
    0xf2967979f979ef80LL, 0xde306f6fa16f5fceLL, 0x3f6d91917e91fcefLL, 0xa4f852525552aa07LL,
    0xc04760609d6027fdLL, 0x6535bcbccabc8976LL, 0x2b379b9b569baccdLL, 0x018a8e8e028e048cLL,
    0x5bd2a3a3b6a37115LL, 0x186c0c0c300c603cLL, 0xf6847b7bf17bff8aLL, 0x6a803535d435b5e1LL,
    0x3af51d1d741de869LL, 0xddb3e0e0a7e05347LL, 0xb321d7d77bd7f6acLL, 0x999cc2c22fc25eedLL,
    0x5c432e2eb82e6d96LL, 0x96294b4b314b627aLL, 0xe15dfefedffea321LL, 0xaed5575741578216LL,
    0x2abd15155415a841LL, 0xeee87777c1779fb6LL, 0x6e923737dc37a5ebLL, 0xd79ee5e5b3e57b56LL,
    0x23139f9f469f8cd9LL, 0xfd23f0f0e7f0d317LL, 0x94204a4a354a6a7fLL, 0xa944dada4fda9e95LL,
    0xb0a258587d58fa25LL, 0x8fcfc9c903c906caLL, 0x527c2929a429558dLL, 0x145a0a0a280a5022LL,
    0x7f50b1b1feb1e14fLL, 0x5dc9a0a0baa0691aLL, 0xd6146b6bb16b7fdaLL, 0x17d985852e855cabLL,
    0x673cbdbdcebd8173LL, 0xba8f5d5d695dd234LL, 0x2090101040108050LL, 0xf507f4f4f7f4f303LL,
    0x8bddcbcb0bcb16c0LL, 0x7cd33e3ef83eedc6LL, 0x0a2d050514052811LL, 0xce78676781671fe6LL,
    0xd597e4e4b7e47353LL, 0x4e0227279c2725bbLL, 0x8273414119413258LL, 0x0ba78b8b168b2c9dLL,
    0x53f6a7a7a6a75101LL, 0xfab27d7de97dcf94LL, 0x374995956e95dcfbLL, 0xad56d8d847d88e9fLL,
    0xeb70fbfbcbfb8b30LL, 0xc1cdeeee9fee2371LL, 0xf8bb7c7ced7cc791LL, 0xcc716666856617e3LL,
    0xa77bdddd53dda68eLL, 0x2eaf17175c17b84bLL, 0x8e45474701470246LL, 0x211a9e9e429e84dcLL,
    0x89d4caca0fca1ec5LL, 0x5a582d2db42d7599LL, 0x632ebfbfc6bf9179LL, 0x0e3f07071c07381bLL,
    0x47acadad8ead0123LL, 0xb4b05a5a755aea2fLL, 0x1bef838336836cb5LL, 0x66b63333cc3385ffLL,
    0xc65c636391633ff2LL, 0x041202020802100aLL, 0x4993aaaa92aa3938LL, 0xe2de7171d971afa8LL,
    0x8dc6c8c807c80ecfLL, 0x32d119196419c87dLL, 0x923b494939497270LL, 0xaf5fd9d943d9869aLL,
    0xf931f2f2eff2c31dLL, 0xdba8e3e3abe34b48LL, 0xb6b95b5b715be22aLL, 0x0dbc88881a883492LL,
    0x293e9a9a529aa4c8LL, 0x4c0b262698262dbeLL, 0x64bf3232c8328dfaLL, 0x7d59b0b0fab0e94aLL,
    0xcff2e9e983e91b6aLL, 0x1e770f0f3c0f7833LL, 0xb733d5d573d5e6a6LL, 0x1df480803a8074baLL,
    0x6127bebec2be997cLL, 0x87ebcdcd13cd26deLL, 0x68893434d034bde4LL, 0x903248483d487a75LL,
    0xe354ffffdbffab24LL, 0xf48d7a7af57af78fLL, 0x3d6490907a90f4eaLL, 0xbe9d5f5f615fc23eLL,
    0x403d202080201da0LL, 0xd00f6868bd6867d5LL, 0x34ca1a1a681ad072LL, 0x41b7aeae82ae192cLL,
    0x757db4b4eab4c95eLL, 0xa8ce54544d549a19LL, 0x3b7f93937693ece5LL, 0x442f222288220daaLL,
    0xc86364648d6407e9LL, 0xff2af1f1e3f1db12LL, 0xe6cc7373d173bfa2LL, 0x248212124812905aLL,
    0x807a40401d403a5dLL, 0x1048080820084028LL, 0x9b95c3c32bc356e8LL, 0xc5dfecec97ec337bLL,
    0xab4ddbdb4bdb9690LL, 0x5fc0a1a1bea1611fLL, 0x07918d8d0e8d1c83LL, 0x7ac83d3df43df5c9LL,
    0x335b97976697ccf1LL, 0x0000000000000000LL, 0x83f9cfcf1bcf36d4LL, 0x566e2b2bac2b4587LL,
    0xece17676c57697b3LL, 0x19e68282328264b0LL, 0xb128d6d67fd6fea9LL, 0x36c31b1b6c1bd877LL,
    0x7774b5b5eeb5c15bLL, 0x43beafaf86af1129LL, 0xd41d6a6ab56a77dfLL, 0xa0ea50505d50ba0dLL,
    0x8a5745450945124cLL, 0xfb38f3f3ebf3cb18LL, 0x60ad3030c0309df0LL, 0xc3c4efef9bef2b74LL,
    0x7eda3f3ffc3fe5c3LL, 0xaac755554955921cLL, 0x59dba2a2b2a27910LL, 0xc9e9eaea8fea0365LL,
    0xca6a656589650fecLL, 0x6903babad2bab968LL, 0x5e4a2f2fbc2f6593LL, 0x9d8ec0c027c04ee7LL,
    0xa160dede5fdebe81LL, 0x38fc1c1c701ce06cLL, 0xe746fdfdd3fdbb2eLL, 0x9a1f4d4d294d5264LL,
    0x397692927292e4e0LL, 0xeafa7575c9758fbcLL, 0x0c3606061806301eLL, 0x09ae8a8a128a2498LL,
    0x794bb2b2f2b2f940LL, 0xd185e6e6bfe66359LL, 0x1c7e0e0e380e7036LL, 0x3ee71f1f7c1ff863LL,
    0xc4556262956237f7LL, 0xb53ad4d477d4eea3LL, 0x4d81a8a89aa82932LL, 0x315296966296c4f4LL,
    0xef62f9f9c3f99b3aLL, 0x97a3c5c533c566f6LL, 0x4a102525942535b1LL, 0xb2ab59597959f220LL,
    0x15d084842a8454aeLL, 0xe4c57272d572b7a7LL, 0x72ec3939e439d5ddLL, 0x98164c4c2d4c5a61LL,
    0xbc945e5e655eca3bLL, 0xf09f7878fd78e785LL, 0x70e53838e038ddd8LL, 0x05988c8c0a8c1486LL,
    0xbf17d1d163d1c6b2LL, 0x57e4a5a5aea5410bLL, 0xd9a1e2e2afe2434dLL, 0xc24e616199612ff8LL,
    0x7b42b3b3f6b3f145LL, 0x42342121842115a5LL, 0x25089c9c4a9c94d6LL, 0x3cee1e1e781ef066LL,
    0x8661434311432252LL, 0x93b1c7c73bc776fcLL, 0xe54ffcfcd7fcb32bLL, 0x0824040410042014LL,
    0xa2e351515951b208LL, 0x2f2599995e99bcc7LL, 0xda226d6da96d4fc4LL, 0x1a650d0d340d6839LL,
    0xe979fafacffa8335LL, 0xa369dfdf5bdfb684LL, 0xfca97e7ee57ed79bLL, 0x4819242490243db4LL,
    0x76fe3b3bec3bc5d7LL, 0x4b9aabab96ab313dLL, 0x81f0cece1fce3ed1LL, 0x2299111144118855LL,
    0x03838f8f068f0c89LL, 0x9c044e4e254e4a6bLL, 0x7366b7b7e6b7d151LL, 0xcbe0ebeb8beb0b60LL,
    0x78c13c3cf03cfdccLL, 0x1ffd81813e817cbfLL, 0x354094946a94d4feLL, 0xf31cf7f7fbf7eb0cLL,
    0x6f18b9b9deb9a167LL, 0x268b13134c13985fLL, 0x58512c2cb02c7d9cLL, 0xbb05d3d36bd3d6b8LL,
    0xd38ce7e7bbe76b5cLL, 0xdc396e6ea56e57cbLL, 0x95aac4c437c46ef3LL, 0x061b03030c03180fLL,
    0xacdc565645568a13LL, 0x885e44440d441a49LL, 0xfea07f7fe17fdf9eLL, 0x4f88a9a99ea92137LL,
    0x54672a2aa82a4d82LL, 0x6b0abbbbd6bbb16dLL, 0x9f87c1c123c146e2LL, 0xa6f153535153a202LL,
    0xa572dcdc57dcae8bLL, 0x16530b0b2c0b5827LL, 0x27019d9d4e9d9cd3LL, 0xd82b6c6cad6c47c1LL,
    0x62a43131c43195f5LL, 0xe8f37474cd7487b9LL, 0xf115f6f6fff6e309LL, 0x8c4c464605460a43LL,
    0x45a5acac8aac0926LL, 0x0fb589891e893c97LL, 0x28b414145014a044LL, 0xdfbae1e1a3e15b42LL,
    0x2ca616165816b04eLL, 0x74f73a3ae83acdd2LL, 0xd2066969b9696fd0LL, 0x124109092409482dLL,
    0xe0d77070dd70a7adLL, 0x716fb6b6e2b6d954LL, 0xbd1ed0d067d0ceb7LL, 0xc7d6eded93ed3b7eLL,
    0x85e2cccc17cc2edbLL, 0x8468424215422a57LL, 0x2d2c98985a98b4c2LL, 0x55eda4a4aaa4490eLL,
    0x50752828a0285d88LL, 0xb8865c5c6d5cda31LL, 0xed6bf8f8c7f8933fLL, 0x11c28686228644a4LL,
};

static const uint64_t c_3[0x100] = {
    0x7830d818186018c0LL, 0xaf462623238c2305LL, 0xf991b8c6c63fc67eLL, 0x6fcdfbe8e887e813LL,
    0xa113cb878726874cLL, 0x626d11b8b8dab8a9LL, 0x0502090101040108LL, 0x6e9e0d4f4f214f42LL,
    0xee6c9b3636d836adLL, 0x0451ffa6a6a2a659LL, 0xbdb90cd2d26fd2deLL, 0x06f70ef5f5f3f5fbLL,
    0x80f2967979f979efLL, 0xcede306f6fa16f5fLL, 0xef3f6d91917e91fcLL, 0x07a4f852525552aaLL,
    0xfdc04760609d6027LL, 0x766535bcbccabc89LL, 0xcd2b379b9b569bacLL, 0x8c018a8e8e028e04LL,
    0x155bd2a3a3b6a371LL, 0x3c186c0c0c300c60LL, 0x8af6847b7bf17bffLL, 0xe16a803535d435b5LL,
    0x693af51d1d741de8LL, 0x47ddb3e0e0a7e053LL, 0xacb321d7d77bd7f6LL, 0xed999cc2c22fc25eLL,
    0x965c432e2eb82e6dLL, 0x7a96294b4b314b62LL, 0x21e15dfefedffea3LL, 0x16aed55757415782LL,
    0x412abd15155415a8LL, 0xb6eee87777c1779fLL, 0xeb6e923737dc37a5LL, 0x56d79ee5e5b3e57bLL,
    0xd923139f9f469f8cLL, 0x17fd23f0f0e7f0d3LL, 0x7f94204a4a354a6aLL, 0x95a944dada4fda9eLL,
    0x25b0a258587d58faLL, 0xca8fcfc9c903c906LL, 0x8d527c2929a42955LL, 0x22145a0a0a280a50LL,
    0x4f7f50b1b1feb1e1LL, 0x1a5dc9a0a0baa069LL, 0xdad6146b6bb16b7fLL, 0xab17d985852e855cLL,
    0x73673cbdbdcebd81LL, 0x34ba8f5d5d695dd2LL, 0x5020901010401080LL, 0x03f507f4f4f7f4f3LL,
    0xc08bddcbcb0bcb16LL, 0xc67cd33e3ef83eedLL, 0x110a2d0505140528LL, 0xe6ce78676781671fLL,
    0x53d597e4e4b7e473LL, 0xbb4e0227279c2725LL, 0x5882734141194132LL, 0x9d0ba78b8b168b2cLL,
    0x0153f6a7a7a6a751LL, 0x94fab27d7de97dcfLL, 0xfb374995956e95dcLL, 0x9fad56d8d847d88eLL,
    0x30eb70fbfbcbfb8bLL, 0x71c1cdeeee9fee23LL, 0x91f8bb7c7ced7cc7LL, 0xe3cc716666856617LL,
    0x8ea77bdddd53dda6LL, 0x4b2eaf17175c17b8LL, 0x468e454747014702LL, 0xdc211a9e9e429e84LL,
    0xc589d4caca0fca1eLL, 0x995a582d2db42d75LL, 0x79632ebfbfc6bf91LL, 0x1b0e3f07071c0738LL,
    0x2347acadad8ead01LL, 0x2fb4b05a5a755aeaLL, 0xb51bef838336836cLL, 0xff66b63333cc3385LL,
    0xf2c65c636391633fLL, 0x0a04120202080210LL, 0x384993aaaa92aa39LL, 0xa8e2de7171d971afLL,
    0xcf8dc6c8c807c80eLL, 0x7d32d119196419c8LL, 0x70923b4949394972LL, 0x9aaf5fd9d943d986LL,
    0x1df931f2f2eff2c3LL, 0x48dba8e3e3abe34bLL, 0x2ab6b95b5b715be2LL, 0x920dbc88881a8834LL,
    0xc8293e9a9a529aa4LL, 0xbe4c0b262698262dLL, 0xfa64bf3232c8328dLL, 0x4a7d59b0b0fab0e9LL,
    0x6acff2e9e983e91bLL, 0x331e770f0f3c0f78LL, 0xa6b733d5d573d5e6LL, 0xba1df480803a8074LL,
    0x7c6127bebec2be99LL, 0xde87ebcdcd13cd26LL, 0xe468893434d034bdLL, 0x75903248483d487aLL,
    0x24e354ffffdbffabLL, 0x8ff48d7a7af57af7LL, 0xea3d6490907a90f4LL, 0x3ebe9d5f5f615fc2LL,
    0xa0403d202080201dLL, 0xd5d00f6868bd6867LL, 0x7234ca1a1a681ad0LL, 0x2c41b7aeae82ae19LL,
    0x5e757db4b4eab4c9LL, 0x19a8ce54544d549aLL, 0xe53b7f93937693ecLL, 0xaa442f222288220dLL,
    0xe9c86364648d6407LL, 0x12ff2af1f1e3f1dbLL, 0xa2e6cc7373d173bfLL, 0x5a24821212481290LL,
    0x5d807a40401d403aLL, 0x2810480808200840LL, 0xe89b95c3c32bc356LL, 0x7bc5dfecec97ec33LL,
    0x90ab4ddbdb4bdb96LL, 0x1f5fc0a1a1bea161LL, 0x8307918d8d0e8d1cLL, 0xc97ac83d3df43df5LL,
    0xf1335b97976697ccLL, 0x0000000000000000LL, 0xd483f9cfcf1bcf36LL, 0x87566e2b2bac2b45LL,
    0xb3ece17676c57697LL, 0xb019e68282328264LL, 0xa9b128d6d67fd6feLL, 0x7736c31b1b6c1bd8LL,
    0x5b7774b5b5eeb5c1LL, 0x2943beafaf86af11LL, 0xdfd41d6a6ab56a77LL, 0x0da0ea50505d50baLL,
    0x4c8a574545094512LL, 0x18fb38f3f3ebf3cbLL, 0xf060ad3030c0309dLL, 0x74c3c4efef9bef2bLL,
    0xc37eda3f3ffc3fe5LL, 0x1caac75555495592LL, 0x1059dba2a2b2a279LL, 0x65c9e9eaea8fea03LL,
    0xecca6a656589650fLL, 0x686903babad2bab9LL, 0x935e4a2f2fbc2f65LL, 0xe79d8ec0c027c04eLL,
    0x81a160dede5fdebeLL, 0x6c38fc1c1c701ce0LL, 0x2ee746fdfdd3fdbbLL, 0x649a1f4d4d294d52LL,
    0xe0397692927292e4LL, 0xbceafa7575c9758fLL, 0x1e0c360606180630LL, 0x9809ae8a8a128a24LL,
    0x40794bb2b2f2b2f9LL, 0x59d185e6e6bfe663LL, 0x361c7e0e0e380e70LL, 0x633ee71f1f7c1ff8LL,
    0xf7c4556262956237LL, 0xa3b53ad4d477d4eeLL, 0x324d81a8a89aa829LL, 0xf4315296966296c4LL,
    0x3aef62f9f9c3f99bLL, 0xf697a3c5c533c566LL, 0xb14a102525942535LL, 0x20b2ab59597959f2LL,
    0xae15d084842a8454LL, 0xa7e4c57272d572b7LL, 0xdd72ec3939e439d5LL, 0x6198164c4c2d4c5aLL,
    0x3bbc945e5e655ecaLL, 0x85f09f7878fd78e7LL, 0xd870e53838e038ddLL, 0x8605988c8c0a8c14LL,
    0xb2bf17d1d163d1c6LL, 0x0b57e4a5a5aea541LL, 0x4dd9a1e2e2afe243LL, 0xf8c24e616199612fLL,
    0x457b42b3b3f6b3f1LL, 0xa542342121842115LL, 0xd625089c9c4a9c94LL, 0x663cee1e1e781ef0LL,
    0x5286614343114322LL, 0xfc93b1c7c73bc776LL, 0x2be54ffcfcd7fcb3LL, 0x1408240404100420LL,
    0x08a2e351515951b2LL, 0xc72f2599995e99bcLL, 0xc4da226d6da96d4fLL, 0x391a650d0d340d68LL,
    0x35e979fafacffa83LL, 0x84a369dfdf5bdfb6LL, 0x9bfca97e7ee57ed7LL, 0xb44819242490243dLL,
    0xd776fe3b3bec3bc5LL, 0x3d4b9aabab96ab31LL, 0xd181f0cece1fce3eLL, 0x5522991111441188LL,
    0x8903838f8f068f0cLL, 0x6b9c044e4e254e4aLL, 0x517366b7b7e6b7d1LL, 0x60cbe0ebeb8beb0bLL,
    0xcc78c13c3cf03cfdLL, 0xbf1ffd81813e817cLL, 0xfe354094946a94d4LL, 0x0cf31cf7f7fbf7ebLL,
    0x676f18b9b9deb9a1LL, 0x5f268b13134c1398LL, 0x9c58512c2cb02c7dLL, 0xb8bb05d3d36bd3d6LL,
    0x5cd38ce7e7bbe76bLL, 0xcbdc396e6ea56e57LL, 0xf395aac4c437c46eLL, 0x0f061b03030c0318LL,
    0x13acdc565645568aLL, 0x49885e44440d441aLL, 0x9efea07f7fe17fdfLL, 0x374f88a9a99ea921LL,
    0x8254672a2aa82a4dLL, 0x6d6b0abbbbd6bbb1LL, 0xe29f87c1c123c146LL, 0x02a6f153535153a2LL,
    0x8ba572dcdc57dcaeLL, 0x2716530b0b2c0b58LL, 0xd327019d9d4e9d9cLL, 0xc1d82b6c6cad6c47LL,
    0xf562a43131c43195LL, 0xb9e8f37474cd7487LL, 0x09f115f6f6fff6e3LL, 0x438c4c464605460aLL,
    0x2645a5acac8aac09LL, 0x970fb589891e893cLL, 0x4428b414145014a0LL, 0x42dfbae1e1a3e15bLL,
    0x4e2ca616165816b0LL, 0xd274f73a3ae83acdLL, 0xd0d2066969b9696fLL, 0x2d12410909240948LL,
    0xade0d77070dd70a7LL, 0x54716fb6b6e2b6d9LL, 0xb7bd1ed0d067d0ceLL, 0x7ec7d6eded93ed3bLL,
    0xdb85e2cccc17cc2eLL, 0x578468424215422aLL, 0xc22d2c98985a98b4LL, 0x0e55eda4a4aaa449LL,
    0x8850752828a0285dLL, 0x31b8865c5c6d5cdaLL, 0x3fed6bf8f8c7f893LL, 0xa411c28686228644LL,
};

static const uint64_t c_4[0x100] = {
    0xc07830d818186018LL, 0x05af462623238c23LL, 0x7ef991b8c6c63fc6LL, 0x136fcdfbe8e887e8LL,
    0x4ca113cb87872687LL, 0xa9626d11b8b8dab8LL, 0x0805020901010401LL, 0x426e9e0d4f4f214fLL,
    0xadee6c9b3636d836LL, 0x590451ffa6a6a2a6LL, 0xdebdb90cd2d26fd2LL, 0xfb06f70ef5f5f3f5LL,
    0xef80f2967979f979LL, 0x5fcede306f6fa16fLL, 0xfcef3f6d91917e91LL, 0xaa07a4f852525552LL,
    0x27fdc04760609d60LL, 0x89766535bcbccabcLL, 0xaccd2b379b9b569bLL, 0x048c018a8e8e028eLL,
    0x71155bd2a3a3b6a3LL, 0x603c186c0c0c300cLL, 0xff8af6847b7bf17bLL, 0xb5e16a803535d435LL,
    0xe8693af51d1d741dLL, 0x5347ddb3e0e0a7e0LL, 0xf6acb321d7d77bd7LL, 0x5eed999cc2c22fc2LL,
    0x6d965c432e2eb82eLL, 0x627a96294b4b314bLL, 0xa321e15dfefedffeLL, 0x8216aed557574157LL,
    0xa8412abd15155415LL, 0x9fb6eee87777c177LL, 0xa5eb6e923737dc37LL, 0x7b56d79ee5e5b3e5LL,
    0x8cd923139f9f469fLL, 0xd317fd23f0f0e7f0LL, 0x6a7f94204a4a354aLL, 0x9e95a944dada4fdaLL,
    0xfa25b0a258587d58LL, 0x06ca8fcfc9c903c9LL, 0x558d527c2929a429LL, 0x5022145a0a0a280aLL,
    0xe14f7f50b1b1feb1LL, 0x691a5dc9a0a0baa0LL, 0x7fdad6146b6bb16bLL, 0x5cab17d985852e85LL,
    0x8173673cbdbdcebdLL, 0xd234ba8f5d5d695dLL, 0x8050209010104010LL, 0xf303f507f4f4f7f4LL,
    0x16c08bddcbcb0bcbLL, 0xedc67cd33e3ef83eLL, 0x28110a2d05051405LL, 0x1fe6ce7867678167LL,
    0x7353d597e4e4b7e4LL, 0x25bb4e0227279c27LL, 0x3258827341411941LL, 0x2c9d0ba78b8b168bLL,
    0x510153f6a7a7a6a7LL, 0xcf94fab27d7de97dLL, 0xdcfb374995956e95LL, 0x8e9fad56d8d847d8LL,
    0x8b30eb70fbfbcbfbLL, 0x2371c1cdeeee9feeLL, 0xc791f8bb7c7ced7cLL, 0x17e3cc7166668566LL,
    0xa68ea77bdddd53ddLL, 0xb84b2eaf17175c17LL, 0x02468e4547470147LL, 0x84dc211a9e9e429eLL,
    0x1ec589d4caca0fcaLL, 0x75995a582d2db42dLL, 0x9179632ebfbfc6bfLL, 0x381b0e3f07071c07LL,
    0x012347acadad8eadLL, 0xea2fb4b05a5a755aLL, 0x6cb51bef83833683LL, 0x85ff66b63333cc33LL,
    0x3ff2c65c63639163LL, 0x100a041202020802LL, 0x39384993aaaa92aaLL, 0xafa8e2de7171d971LL,
    0x0ecf8dc6c8c807c8LL, 0xc87d32d119196419LL, 0x7270923b49493949LL, 0x869aaf5fd9d943d9LL,
    0xc31df931f2f2eff2LL, 0x4b48dba8e3e3abe3LL, 0xe22ab6b95b5b715bLL, 0x34920dbc88881a88LL,
    0xa4c8293e9a9a529aLL, 0x2dbe4c0b26269826LL, 0x8dfa64bf3232c832LL, 0xe94a7d59b0b0fab0LL,
    0x1b6acff2e9e983e9LL, 0x78331e770f0f3c0fLL, 0xe6a6b733d5d573d5LL, 0x74ba1df480803a80LL,
    0x997c6127bebec2beLL, 0x26de87ebcdcd13cdLL, 0xbde468893434d034LL, 0x7a75903248483d48LL,
    0xab24e354ffffdbffLL, 0xf78ff48d7a7af57aLL, 0xf4ea3d6490907a90LL, 0xc23ebe9d5f5f615fLL,
    0x1da0403d20208020LL, 0x67d5d00f6868bd68LL, 0xd07234ca1a1a681aLL, 0x192c41b7aeae82aeLL,
    0xc95e757db4b4eab4LL, 0x9a19a8ce54544d54LL, 0xece53b7f93937693LL, 0x0daa442f22228822LL,
    0x07e9c86364648d64LL, 0xdb12ff2af1f1e3f1LL, 0xbfa2e6cc7373d173LL, 0x905a248212124812LL,
    0x3a5d807a40401d40LL, 0x4028104808082008LL, 0x56e89b95c3c32bc3LL, 0x337bc5dfecec97ecLL,
    0x9690ab4ddbdb4bdbLL, 0x611f5fc0a1a1bea1LL, 0x1c8307918d8d0e8dLL, 0xf5c97ac83d3df43dLL,
    0xccf1335b97976697LL, 0x0000000000000000LL, 0x36d483f9cfcf1bcfLL, 0x4587566e2b2bac2bLL,
    0x97b3ece17676c576LL, 0x64b019e682823282LL, 0xfea9b128d6d67fd6LL, 0xd87736c31b1b6c1bLL,
    0xc15b7774b5b5eeb5LL, 0x112943beafaf86afLL, 0x77dfd41d6a6ab56aLL, 0xba0da0ea50505d50LL,
    0x124c8a5745450945LL, 0xcb18fb38f3f3ebf3LL, 0x9df060ad3030c030LL, 0x2b74c3c4efef9befLL,
    0xe5c37eda3f3ffc3fLL, 0x921caac755554955LL, 0x791059dba2a2b2a2LL, 0x0365c9e9eaea8feaLL,
    0x0fecca6a65658965LL, 0xb9686903babad2baLL, 0x65935e4a2f2fbc2fLL, 0x4ee79d8ec0c027c0LL,
    0xbe81a160dede5fdeLL, 0xe06c38fc1c1c701cLL, 0xbb2ee746fdfdd3fdLL, 0x52649a1f4d4d294dLL,
    0xe4e0397692927292LL, 0x8fbceafa7575c975LL, 0x301e0c3606061806LL, 0x249809ae8a8a128aLL,
    0xf940794bb2b2f2b2LL, 0x6359d185e6e6bfe6LL, 0x70361c7e0e0e380eLL, 0xf8633ee71f1f7c1fLL,
    0x37f7c45562629562LL, 0xeea3b53ad4d477d4LL, 0x29324d81a8a89aa8LL, 0xc4f4315296966296LL,
    0x9b3aef62f9f9c3f9LL, 0x66f697a3c5c533c5LL, 0x35b14a1025259425LL, 0xf220b2ab59597959LL,
    0x54ae15d084842a84LL, 0xb7a7e4c57272d572LL, 0xd5dd72ec3939e439LL, 0x5a6198164c4c2d4cLL,
    0xca3bbc945e5e655eLL, 0xe785f09f7878fd78LL, 0xddd870e53838e038LL, 0x148605988c8c0a8cLL,
    0xc6b2bf17d1d163d1LL, 0x410b57e4a5a5aea5LL, 0x434dd9a1e2e2afe2LL, 0x2ff8c24e61619961LL,
    0xf1457b42b3b3f6b3LL, 0x15a5423421218421LL, 0x94d625089c9c4a9cLL, 0xf0663cee1e1e781eLL,
    0x2252866143431143LL, 0x76fc93b1c7c73bc7LL, 0xb32be54ffcfcd7fcLL, 0x2014082404041004LL,
    0xb208a2e351515951LL, 0xbcc72f2599995e99LL, 0x4fc4da226d6da96dLL, 0x68391a650d0d340dLL,
    0x8335e979fafacffaLL, 0xb684a369dfdf5bdfLL, 0xd79bfca97e7ee57eLL, 0x3db4481924249024LL,
    0xc5d776fe3b3bec3bLL, 0x313d4b9aabab96abLL, 0x3ed181f0cece1fceLL, 0x8855229911114411LL,
    0x0c8903838f8f068fLL, 0x4a6b9c044e4e254eLL, 0xd1517366b7b7e6b7LL, 0x0b60cbe0ebeb8bebLL,
    0xfdcc78c13c3cf03cLL, 0x7cbf1ffd81813e81LL, 0xd4fe354094946a94LL, 0xeb0cf31cf7f7fbf7LL,
    0xa1676f18b9b9deb9LL, 0x985f268b13134c13LL, 0x7d9c58512c2cb02cLL, 0xd6b8bb05d3d36bd3LL,
    0x6b5cd38ce7e7bbe7LL, 0x57cbdc396e6ea56eLL, 0x6ef395aac4c437c4LL, 0x180f061b03030c03LL,
    0x8a13acdc56564556LL, 0x1a49885e44440d44LL, 0xdf9efea07f7fe17fLL, 0x21374f88a9a99ea9LL,
    0x4d8254672a2aa82aLL, 0xb16d6b0abbbbd6bbLL, 0x46e29f87c1c123c1LL, 0xa202a6f153535153LL,
    0xae8ba572dcdc57dcLL, 0x582716530b0b2c0bLL, 0x9cd327019d9d4e9dLL, 0x47c1d82b6c6cad6cLL,
    0x95f562a43131c431LL, 0x87b9e8f37474cd74LL, 0xe309f115f6f6fff6LL, 0x0a438c4c46460546LL,
    0x092645a5acac8aacLL, 0x3c970fb589891e89LL, 0xa04428b414145014LL, 0x5b42dfbae1e1a3e1LL,
    0xb04e2ca616165816LL, 0xcdd274f73a3ae83aLL, 0x6fd0d2066969b969LL, 0x482d124109092409LL,
    0xa7ade0d77070dd70LL, 0xd954716fb6b6e2b6LL, 0xceb7bd1ed0d067d0LL, 0x3b7ec7d6eded93edLL,
    0x2edb85e2cccc17ccLL, 0x2a57846842421542LL, 0xb4c22d2c98985a98LL, 0x490e55eda4a4aaa4LL,
    0x5d8850752828a028LL, 0xda31b8865c5c6d5cLL, 0x933fed6bf8f8c7f8LL, 0x44a411c286862286LL,
};

static const uint64_t c_5[0x100] = {
    0x18c07830d8181860LL, 0x2305af462623238cLL, 0xc67ef991b8c6c63fLL, 0xe8136fcdfbe8e887LL,
    0x874ca113cb878726LL, 0xb8a9626d11b8b8daLL, 0x0108050209010104LL, 0x4f426e9e0d4f4f21LL,
    0x36adee6c9b3636d8LL, 0xa6590451ffa6a6a2LL, 0xd2debdb90cd2d26fLL, 0xf5fb06f70ef5f5f3LL,
    0x79ef80f2967979f9LL, 0x6f5fcede306f6fa1LL, 0x91fcef3f6d91917eLL, 0x52aa07a4f8525255LL,
    0x6027fdc04760609dLL, 0xbc89766535bcbccaLL, 0x9baccd2b379b9b56LL, 0x8e048c018a8e8e02LL,
    0xa371155bd2a3a3b6LL, 0x0c603c186c0c0c30LL, 0x7bff8af6847b7bf1LL, 0x35b5e16a803535d4LL,
    0x1de8693af51d1d74LL, 0xe05347ddb3e0e0a7LL, 0xd7f6acb321d7d77bLL, 0xc25eed999cc2c22fLL,
    0x2e6d965c432e2eb8LL, 0x4b627a96294b4b31LL, 0xfea321e15dfefedfLL, 0x578216aed5575741LL,
    0x15a8412abd151554LL, 0x779fb6eee87777c1LL, 0x37a5eb6e923737dcLL, 0xe57b56d79ee5e5b3LL,
    0x9f8cd923139f9f46LL, 0xf0d317fd23f0f0e7LL, 0x4a6a7f94204a4a35LL, 0xda9e95a944dada4fLL,
    0x58fa25b0a258587dLL, 0xc906ca8fcfc9c903LL, 0x29558d527c2929a4LL, 0x0a5022145a0a0a28LL,
    0xb1e14f7f50b1b1feLL, 0xa0691a5dc9a0a0baLL, 0x6b7fdad6146b6bb1LL, 0x855cab17d985852eLL,
    0xbd8173673cbdbdceLL, 0x5dd234ba8f5d5d69LL, 0x1080502090101040LL, 0xf4f303f507f4f4f7LL,
    0xcb16c08bddcbcb0bLL, 0x3eedc67cd33e3ef8LL, 0x0528110a2d050514LL, 0x671fe6ce78676781LL,
    0xe47353d597e4e4b7LL, 0x2725bb4e0227279cLL, 0x4132588273414119LL, 0x8b2c9d0ba78b8b16LL,
    0xa7510153f6a7a7a6LL, 0x7dcf94fab27d7de9LL, 0x95dcfb374995956eLL, 0xd88e9fad56d8d847LL,
    0xfb8b30eb70fbfbcbLL, 0xee2371c1cdeeee9fLL, 0x7cc791f8bb7c7cedLL, 0x6617e3cc71666685LL,
    0xdda68ea77bdddd53LL, 0x17b84b2eaf17175cLL, 0x4702468e45474701LL, 0x9e84dc211a9e9e42LL,
    0xca1ec589d4caca0fLL, 0x2d75995a582d2db4LL, 0xbf9179632ebfbfc6LL, 0x07381b0e3f07071cLL,
    0xad012347acadad8eLL, 0x5aea2fb4b05a5a75LL, 0x836cb51bef838336LL, 0x3385ff66b63333ccLL,
    0x633ff2c65c636391LL, 0x02100a0412020208LL, 0xaa39384993aaaa92LL, 0x71afa8e2de7171d9LL,
    0xc80ecf8dc6c8c807LL, 0x19c87d32d1191964LL, 0x497270923b494939LL, 0xd9869aaf5fd9d943LL,
    0xf2c31df931f2f2efLL, 0xe34b48dba8e3e3abLL, 0x5be22ab6b95b5b71LL, 0x8834920dbc88881aLL,
    0x9aa4c8293e9a9a52LL, 0x262dbe4c0b262698LL, 0x328dfa64bf3232c8LL, 0xb0e94a7d59b0b0faLL,
    0xe91b6acff2e9e983LL, 0x0f78331e770f0f3cLL, 0xd5e6a6b733d5d573LL, 0x8074ba1df480803aLL,
    0xbe997c6127bebec2LL, 0xcd26de87ebcdcd13LL, 0x34bde468893434d0LL, 0x487a75903248483dLL,
    0xffab24e354ffffdbLL, 0x7af78ff48d7a7af5LL, 0x90f4ea3d6490907aLL, 0x5fc23ebe9d5f5f61LL,
    0x201da0403d202080LL, 0x6867d5d00f6868bdLL, 0x1ad07234ca1a1a68LL, 0xae192c41b7aeae82LL,
    0xb4c95e757db4b4eaLL, 0x549a19a8ce54544dLL, 0x93ece53b7f939376LL, 0x220daa442f222288LL,
    0x6407e9c86364648dLL, 0xf1db12ff2af1f1e3LL, 0x73bfa2e6cc7373d1LL, 0x12905a2482121248LL,
    0x403a5d807a40401dLL, 0x0840281048080820LL, 0xc356e89b95c3c32bLL, 0xec337bc5dfecec97LL,
    0xdb9690ab4ddbdb4bLL, 0xa1611f5fc0a1a1beLL, 0x8d1c8307918d8d0eLL, 0x3df5c97ac83d3df4LL,
    0x97ccf1335b979766LL, 0x0000000000000000LL, 0xcf36d483f9cfcf1bLL, 0x2b4587566e2b2bacLL,
    0x7697b3ece17676c5LL, 0x8264b019e6828232LL, 0xd6fea9b128d6d67fLL, 0x1bd87736c31b1b6cLL,
    0xb5c15b7774b5b5eeLL, 0xaf112943beafaf86LL, 0x6a77dfd41d6a6ab5LL, 0x50ba0da0ea50505dLL,
    0x45124c8a57454509LL, 0xf3cb18fb38f3f3ebLL, 0x309df060ad3030c0LL, 0xef2b74c3c4efef9bLL,
    0x3fe5c37eda3f3ffcLL, 0x55921caac7555549LL, 0xa2791059dba2a2b2LL, 0xea0365c9e9eaea8fLL,
    0x650fecca6a656589LL, 0xbab9686903babad2LL, 0x2f65935e4a2f2fbcLL, 0xc04ee79d8ec0c027LL,
    0xdebe81a160dede5fLL, 0x1ce06c38fc1c1c70LL, 0xfdbb2ee746fdfdd3LL, 0x4d52649a1f4d4d29LL,
    0x92e4e03976929272LL, 0x758fbceafa7575c9LL, 0x06301e0c36060618LL, 0x8a249809ae8a8a12LL,
    0xb2f940794bb2b2f2LL, 0xe66359d185e6e6bfLL, 0x0e70361c7e0e0e38LL, 0x1ff8633ee71f1f7cLL,
    0x6237f7c455626295LL, 0xd4eea3b53ad4d477LL, 0xa829324d81a8a89aLL, 0x96c4f43152969662LL,
    0xf99b3aef62f9f9c3LL, 0xc566f697a3c5c533LL, 0x2535b14a10252594LL, 0x59f220b2ab595979LL,
    0x8454ae15d084842aLL, 0x72b7a7e4c57272d5LL, 0x39d5dd72ec3939e4LL, 0x4c5a6198164c4c2dLL,
    0x5eca3bbc945e5e65LL, 0x78e785f09f7878fdLL, 0x38ddd870e53838e0LL, 0x8c148605988c8c0aLL,
    0xd1c6b2bf17d1d163LL, 0xa5410b57e4a5a5aeLL, 0xe2434dd9a1e2e2afLL, 0x612ff8c24e616199LL,
    0xb3f1457b42b3b3f6LL, 0x2115a54234212184LL, 0x9c94d625089c9c4aLL, 0x1ef0663cee1e1e78LL,
    0x4322528661434311LL, 0xc776fc93b1c7c73bLL, 0xfcb32be54ffcfcd7LL, 0x0420140824040410LL,
    0x51b208a2e3515159LL, 0x99bcc72f2599995eLL, 0x6d4fc4da226d6da9LL, 0x0d68391a650d0d34LL,
    0xfa8335e979fafacfLL, 0xdfb684a369dfdf5bLL, 0x7ed79bfca97e7ee5LL, 0x243db44819242490LL,
    0x3bc5d776fe3b3becLL, 0xab313d4b9aabab96LL, 0xce3ed181f0cece1fLL, 0x1188552299111144LL,
    0x8f0c8903838f8f06LL, 0x4e4a6b9c044e4e25LL, 0xb7d1517366b7b7e6LL, 0xeb0b60cbe0ebeb8bLL,
    0x3cfdcc78c13c3cf0LL, 0x817cbf1ffd81813eLL, 0x94d4fe354094946aLL, 0xf7eb0cf31cf7f7fbLL,
    0xb9a1676f18b9b9deLL, 0x13985f268b13134cLL, 0x2c7d9c58512c2cb0LL, 0xd3d6b8bb05d3d36bLL,
    0xe76b5cd38ce7e7bbLL, 0x6e57cbdc396e6ea5LL, 0xc46ef395aac4c437LL, 0x03180f061b03030cLL,
    0x568a13acdc565645LL, 0x441a49885e44440dLL, 0x7fdf9efea07f7fe1LL, 0xa921374f88a9a99eLL,
    0x2a4d8254672a2aa8LL, 0xbbb16d6b0abbbbd6LL, 0xc146e29f87c1c123LL, 0x53a202a6f1535351LL,
    0xdcae8ba572dcdc57LL, 0x0b582716530b0b2cLL, 0x9d9cd327019d9d4eLL, 0x6c47c1d82b6c6cadLL,
    0x3195f562a43131c4LL, 0x7487b9e8f37474cdLL, 0xf6e309f115f6f6ffLL, 0x460a438c4c464605LL,
    0xac092645a5acac8aLL, 0x893c970fb589891eLL, 0x14a04428b4141450LL, 0xe15b42dfbae1e1a3LL,
    0x16b04e2ca6161658LL, 0x3acdd274f73a3ae8LL, 0x696fd0d2066969b9LL, 0x09482d1241090924LL,
    0x70a7ade0d77070ddLL, 0xb6d954716fb6b6e2LL, 0xd0ceb7bd1ed0d067LL, 0xed3b7ec7d6eded93LL,
    0xcc2edb85e2cccc17LL, 0x422a578468424215LL, 0x98b4c22d2c98985aLL, 0xa4490e55eda4a4aaLL,
    0x285d8850752828a0LL, 0x5cda31b8865c5c6dLL, 0xf8933fed6bf8f8c7LL, 0x8644a411c2868622LL,
};

static const uint64_t c_6[0x100] = {
    0x6018c07830d81818LL, 0x8c2305af46262323LL, 0x3fc67ef991b8c6c6LL, 0x87e8136fcdfbe8e8LL,
    0x26874ca113cb8787LL, 0xdab8a9626d11b8b8LL, 0x0401080502090101LL, 0x214f426e9e0d4f4fLL,
    0xd836adee6c9b3636LL, 0xa2a6590451ffa6a6LL, 0x6fd2debdb90cd2d2LL, 0xf3f5fb06f70ef5f5LL,
    0xf979ef80f2967979LL, 0xa16f5fcede306f6fLL, 0x7e91fcef3f6d9191LL, 0x5552aa07a4f85252LL,
    0x9d6027fdc0476060LL, 0xcabc89766535bcbcLL, 0x569baccd2b379b9bLL, 0x028e048c018a8e8eLL,
    0xb6a371155bd2a3a3LL, 0x300c603c186c0c0cLL, 0xf17bff8af6847b7bLL, 0xd435b5e16a803535LL,
    0x741de8693af51d1dLL, 0xa7e05347ddb3e0e0LL, 0x7bd7f6acb321d7d7LL, 0x2fc25eed999cc2c2LL,
    0xb82e6d965c432e2eLL, 0x314b627a96294b4bLL, 0xdffea321e15dfefeLL, 0x41578216aed55757LL,
    0x5415a8412abd1515LL, 0xc1779fb6eee87777LL, 0xdc37a5eb6e923737LL, 0xb3e57b56d79ee5e5LL,
    0x469f8cd923139f9fLL, 0xe7f0d317fd23f0f0LL, 0x354a6a7f94204a4aLL, 0x4fda9e95a944dadaLL,
    0x7d58fa25b0a25858LL, 0x03c906ca8fcfc9c9LL, 0xa429558d527c2929LL, 0x280a5022145a0a0aLL,
    0xfeb1e14f7f50b1b1LL, 0xbaa0691a5dc9a0a0LL, 0xb16b7fdad6146b6bLL, 0x2e855cab17d98585LL,
    0xcebd8173673cbdbdLL, 0x695dd234ba8f5d5dLL, 0x4010805020901010LL, 0xf7f4f303f507f4f4LL,
    0x0bcb16c08bddcbcbLL, 0xf83eedc67cd33e3eLL, 0x140528110a2d0505LL, 0x81671fe6ce786767LL,
    0xb7e47353d597e4e4LL, 0x9c2725bb4e022727LL, 0x1941325882734141LL, 0x168b2c9d0ba78b8bLL,
    0xa6a7510153f6a7a7LL, 0xe97dcf94fab27d7dLL, 0x6e95dcfb37499595LL, 0x47d88e9fad56d8d8LL,
    0xcbfb8b30eb70fbfbLL, 0x9fee2371c1cdeeeeLL, 0xed7cc791f8bb7c7cLL, 0x856617e3cc716666LL,
    0x53dda68ea77bddddLL, 0x5c17b84b2eaf1717LL, 0x014702468e454747LL, 0x429e84dc211a9e9eLL,
    0x0fca1ec589d4cacaLL, 0xb42d75995a582d2dLL, 0xc6bf9179632ebfbfLL, 0x1c07381b0e3f0707LL,
    0x8ead012347acadadLL, 0x755aea2fb4b05a5aLL, 0x36836cb51bef8383LL, 0xcc3385ff66b63333LL,
    0x91633ff2c65c6363LL, 0x0802100a04120202LL, 0x92aa39384993aaaaLL, 0xd971afa8e2de7171LL,
    0x07c80ecf8dc6c8c8LL, 0x6419c87d32d11919LL, 0x39497270923b4949LL, 0x43d9869aaf5fd9d9LL,
    0xeff2c31df931f2f2LL, 0xabe34b48dba8e3e3LL, 0x715be22ab6b95b5bLL, 0x1a8834920dbc8888LL,
    0x529aa4c8293e9a9aLL, 0x98262dbe4c0b2626LL, 0xc8328dfa64bf3232LL, 0xfab0e94a7d59b0b0LL,
    0x83e91b6acff2e9e9LL, 0x3c0f78331e770f0fLL, 0x73d5e6a6b733d5d5LL, 0x3a8074ba1df48080LL,
    0xc2be997c6127bebeLL, 0x13cd26de87ebcdcdLL, 0xd034bde468893434LL, 0x3d487a7590324848LL,
    0xdbffab24e354ffffLL, 0xf57af78ff48d7a7aLL, 0x7a90f4ea3d649090LL, 0x615fc23ebe9d5f5fLL,
    0x80201da0403d2020LL, 0xbd6867d5d00f6868LL, 0x681ad07234ca1a1aLL, 0x82ae192c41b7aeaeLL,
    0xeab4c95e757db4b4LL, 0x4d549a19a8ce5454LL, 0x7693ece53b7f9393LL, 0x88220daa442f2222LL,
    0x8d6407e9c8636464LL, 0xe3f1db12ff2af1f1LL, 0xd173bfa2e6cc7373LL, 0x4812905a24821212LL,
    0x1d403a5d807a4040LL, 0x2008402810480808LL, 0x2bc356e89b95c3c3LL, 0x97ec337bc5dfececLL,
    0x4bdb9690ab4ddbdbLL, 0xbea1611f5fc0a1a1LL, 0x0e8d1c8307918d8dLL, 0xf43df5c97ac83d3dLL,
    0x6697ccf1335b9797LL, 0x0000000000000000LL, 0x1bcf36d483f9cfcfLL, 0xac2b4587566e2b2bLL,
    0xc57697b3ece17676LL, 0x328264b019e68282LL, 0x7fd6fea9b128d6d6LL, 0x6c1bd87736c31b1bLL,
    0xeeb5c15b7774b5b5LL, 0x86af112943beafafLL, 0xb56a77dfd41d6a6aLL, 0x5d50ba0da0ea5050LL,
    0x0945124c8a574545LL, 0xebf3cb18fb38f3f3LL, 0xc0309df060ad3030LL, 0x9bef2b74c3c4efefLL,
    0xfc3fe5c37eda3f3fLL, 0x4955921caac75555LL, 0xb2a2791059dba2a2LL, 0x8fea0365c9e9eaeaLL,
    0x89650fecca6a6565LL, 0xd2bab9686903babaLL, 0xbc2f65935e4a2f2fLL, 0x27c04ee79d8ec0c0LL,
    0x5fdebe81a160dedeLL, 0x701ce06c38fc1c1cLL, 0xd3fdbb2ee746fdfdLL, 0x294d52649a1f4d4dLL,
    0x7292e4e039769292LL, 0xc9758fbceafa7575LL, 0x1806301e0c360606LL, 0x128a249809ae8a8aLL,
    0xf2b2f940794bb2b2LL, 0xbfe66359d185e6e6LL, 0x380e70361c7e0e0eLL, 0x7c1ff8633ee71f1fLL,
    0x956237f7c4556262LL, 0x77d4eea3b53ad4d4LL, 0x9aa829324d81a8a8LL, 0x6296c4f431529696LL,
    0xc3f99b3aef62f9f9LL, 0x33c566f697a3c5c5LL, 0x942535b14a102525LL, 0x7959f220b2ab5959LL,
    0x2a8454ae15d08484LL, 0xd572b7a7e4c57272LL, 0xe439d5dd72ec3939LL, 0x2d4c5a6198164c4cLL,
    0x655eca3bbc945e5eLL, 0xfd78e785f09f7878LL, 0xe038ddd870e53838LL, 0x0a8c148605988c8cLL,
    0x63d1c6b2bf17d1d1LL, 0xaea5410b57e4a5a5LL, 0xafe2434dd9a1e2e2LL, 0x99612ff8c24e6161LL,
    0xf6b3f1457b42b3b3LL, 0x842115a542342121LL, 0x4a9c94d625089c9cLL, 0x781ef0663cee1e1eLL,
    0x1143225286614343LL, 0x3bc776fc93b1c7c7LL, 0xd7fcb32be54ffcfcLL, 0x1004201408240404LL,
    0x5951b208a2e35151LL, 0x5e99bcc72f259999LL, 0xa96d4fc4da226d6dLL, 0x340d68391a650d0dLL,
    0xcffa8335e979fafaLL, 0x5bdfb684a369dfdfLL, 0xe57ed79bfca97e7eLL, 0x90243db448192424LL,
    0xec3bc5d776fe3b3bLL, 0x96ab313d4b9aababLL, 0x1fce3ed181f0ceceLL, 0x4411885522991111LL,
    0x068f0c8903838f8fLL, 0x254e4a6b9c044e4eLL, 0xe6b7d1517366b7b7LL, 0x8beb0b60cbe0ebebLL,
    0xf03cfdcc78c13c3cLL, 0x3e817cbf1ffd8181LL, 0x6a94d4fe35409494LL, 0xfbf7eb0cf31cf7f7LL,
    0xdeb9a1676f18b9b9LL, 0x4c13985f268b1313LL, 0xb02c7d9c58512c2cLL, 0x6bd3d6b8bb05d3d3LL,
    0xbbe76b5cd38ce7e7LL, 0xa56e57cbdc396e6eLL, 0x37c46ef395aac4c4LL, 0x0c03180f061b0303LL,
    0x45568a13acdc5656LL, 0x0d441a49885e4444LL, 0xe17fdf9efea07f7fLL, 0x9ea921374f88a9a9LL,
    0xa82a4d8254672a2aLL, 0xd6bbb16d6b0abbbbLL, 0x23c146e29f87c1c1LL, 0x5153a202a6f15353LL,
    0x57dcae8ba572dcdcLL, 0x2c0b582716530b0bLL, 0x4e9d9cd327019d9dLL, 0xad6c47c1d82b6c6cLL,
    0xc43195f562a43131LL, 0xcd7487b9e8f37474LL, 0xfff6e309f115f6f6LL, 0x05460a438c4c4646LL,
    0x8aac092645a5acacLL, 0x1e893c970fb58989LL, 0x5014a04428b41414LL, 0xa3e15b42dfbae1e1LL,
    0x5816b04e2ca61616LL, 0xe83acdd274f73a3aLL, 0xb9696fd0d2066969LL, 0x2409482d12410909LL,
    0xdd70a7ade0d77070LL, 0xe2b6d954716fb6b6LL, 0x67d0ceb7bd1ed0d0LL, 0x93ed3b7ec7d6ededLL,
    0x17cc2edb85e2ccccLL, 0x15422a5784684242LL, 0x5a98b4c22d2c9898LL, 0xaaa4490e55eda4a4LL,
    0xa0285d8850752828LL, 0x6d5cda31b8865c5cLL, 0xc7f8933fed6bf8f8LL, 0x228644a411c28686LL,
};

static const uint64_t c_7[0x100] = {
    0x186018c07830d818LL, 0x238c2305af462623LL, 0xc63fc67ef991b8c6LL, 0xe887e8136fcdfbe8LL,
    0x8726874ca113cb87LL, 0xb8dab8a9626d11b8LL, 0x0104010805020901LL, 0x4f214f426e9e0d4fLL,
    0x36d836adee6c9b36LL, 0xa6a2a6590451ffa6LL, 0xd26fd2debdb90cd2LL, 0xf5f3f5fb06f70ef5LL,
    0x79f979ef80f29679LL, 0x6fa16f5fcede306fLL, 0x917e91fcef3f6d91LL, 0x525552aa07a4f852LL,
    0x609d6027fdc04760LL, 0xbccabc89766535bcLL, 0x9b569baccd2b379bLL, 0x8e028e048c018a8eLL,
    0xa3b6a371155bd2a3LL, 0x0c300c603c186c0cLL, 0x7bf17bff8af6847bLL, 0x35d435b5e16a8035LL,
    0x1d741de8693af51dLL, 0xe0a7e05347ddb3e0LL, 0xd77bd7f6acb321d7LL, 0xc22fc25eed999cc2LL,
    0x2eb82e6d965c432eLL, 0x4b314b627a96294bLL, 0xfedffea321e15dfeLL, 0x5741578216aed557LL,
    0x155415a8412abd15LL, 0x77c1779fb6eee877LL, 0x37dc37a5eb6e9237LL, 0xe5b3e57b56d79ee5LL,
    0x9f469f8cd923139fLL, 0xf0e7f0d317fd23f0LL, 0x4a354a6a7f94204aLL, 0xda4fda9e95a944daLL,
    0x587d58fa25b0a258LL, 0xc903c906ca8fcfc9LL, 0x29a429558d527c29LL, 0x0a280a5022145a0aLL,
    0xb1feb1e14f7f50b1LL, 0xa0baa0691a5dc9a0LL, 0x6bb16b7fdad6146bLL, 0x852e855cab17d985LL,
    0xbdcebd8173673cbdLL, 0x5d695dd234ba8f5dLL, 0x1040108050209010LL, 0xf4f7f4f303f507f4LL,
    0xcb0bcb16c08bddcbLL, 0x3ef83eedc67cd33eLL, 0x05140528110a2d05LL, 0x6781671fe6ce7867LL,
    0xe4b7e47353d597e4LL, 0x279c2725bb4e0227LL, 0x4119413258827341LL, 0x8b168b2c9d0ba78bLL,
    0xa7a6a7510153f6a7LL, 0x7de97dcf94fab27dLL, 0x956e95dcfb374995LL, 0xd847d88e9fad56d8LL,
    0xfbcbfb8b30eb70fbLL, 0xee9fee2371c1cdeeLL, 0x7ced7cc791f8bb7cLL, 0x66856617e3cc7166LL,
    0xdd53dda68ea77bddLL, 0x175c17b84b2eaf17LL, 0x47014702468e4547LL, 0x9e429e84dc211a9eLL,
    0xca0fca1ec589d4caLL, 0x2db42d75995a582dLL, 0xbfc6bf9179632ebfLL, 0x071c07381b0e3f07LL,
    0xad8ead012347acadLL, 0x5a755aea2fb4b05aLL, 0x8336836cb51bef83LL, 0x33cc3385ff66b633LL,
    0x6391633ff2c65c63LL, 0x020802100a041202LL, 0xaa92aa39384993aaLL, 0x71d971afa8e2de71LL,
    0xc807c80ecf8dc6c8LL, 0x196419c87d32d119LL, 0x4939497270923b49LL, 0xd943d9869aaf5fd9LL,
    0xf2eff2c31df931f2LL, 0xe3abe34b48dba8e3LL, 0x5b715be22ab6b95bLL, 0x881a8834920dbc88LL,
    0x9a529aa4c8293e9aLL, 0x2698262dbe4c0b26LL, 0x32c8328dfa64bf32LL, 0xb0fab0e94a7d59b0LL,
    0xe983e91b6acff2e9LL, 0x0f3c0f78331e770fLL, 0xd573d5e6a6b733d5LL, 0x803a8074ba1df480LL,
    0xbec2be997c6127beLL, 0xcd13cd26de87ebcdLL, 0x34d034bde4688934LL, 0x483d487a75903248LL,
    0xffdbffab24e354ffLL, 0x7af57af78ff48d7aLL, 0x907a90f4ea3d6490LL, 0x5f615fc23ebe9d5fLL,
    0x2080201da0403d20LL, 0x68bd6867d5d00f68LL, 0x1a681ad07234ca1aLL, 0xae82ae192c41b7aeLL,
    0xb4eab4c95e757db4LL, 0x544d549a19a8ce54LL, 0x937693ece53b7f93LL, 0x2288220daa442f22LL,
    0x648d6407e9c86364LL, 0xf1e3f1db12ff2af1LL, 0x73d173bfa2e6cc73LL, 0x124812905a248212LL,
    0x401d403a5d807a40LL, 0x0820084028104808LL, 0xc32bc356e89b95c3LL, 0xec97ec337bc5dfecLL,
    0xdb4bdb9690ab4ddbLL, 0xa1bea1611f5fc0a1LL, 0x8d0e8d1c8307918dLL, 0x3df43df5c97ac83dLL,
    0x976697ccf1335b97LL, 0x0000000000000000LL, 0xcf1bcf36d483f9cfLL, 0x2bac2b4587566e2bLL,
    0x76c57697b3ece176LL, 0x82328264b019e682LL, 0xd67fd6fea9b128d6LL, 0x1b6c1bd87736c31bLL,
    0xb5eeb5c15b7774b5LL, 0xaf86af112943beafLL, 0x6ab56a77dfd41d6aLL, 0x505d50ba0da0ea50LL,
    0x450945124c8a5745LL, 0xf3ebf3cb18fb38f3LL, 0x30c0309df060ad30LL, 0xef9bef2b74c3c4efLL,
    0x3ffc3fe5c37eda3fLL, 0x554955921caac755LL, 0xa2b2a2791059dba2LL, 0xea8fea0365c9e9eaLL,
    0x6589650fecca6a65LL, 0xbad2bab9686903baLL, 0x2fbc2f65935e4a2fLL, 0xc027c04ee79d8ec0LL,
    0xde5fdebe81a160deLL, 0x1c701ce06c38fc1cLL, 0xfdd3fdbb2ee746fdLL, 0x4d294d52649a1f4dLL,
    0x927292e4e0397692LL, 0x75c9758fbceafa75LL, 0x061806301e0c3606LL, 0x8a128a249809ae8aLL,
    0xb2f2b2f940794bb2LL, 0xe6bfe66359d185e6LL, 0x0e380e70361c7e0eLL, 0x1f7c1ff8633ee71fLL,
    0x62956237f7c45562LL, 0xd477d4eea3b53ad4LL, 0xa89aa829324d81a8LL, 0x966296c4f4315296LL,
    0xf9c3f99b3aef62f9LL, 0xc533c566f697a3c5LL, 0x25942535b14a1025LL, 0x597959f220b2ab59LL,
    0x842a8454ae15d084LL, 0x72d572b7a7e4c572LL, 0x39e439d5dd72ec39LL, 0x4c2d4c5a6198164cLL,
    0x5e655eca3bbc945eLL, 0x78fd78e785f09f78LL, 0x38e038ddd870e538LL, 0x8c0a8c148605988cLL,
    0xd163d1c6b2bf17d1LL, 0xa5aea5410b57e4a5LL, 0xe2afe2434dd9a1e2LL, 0x6199612ff8c24e61LL,
    0xb3f6b3f1457b42b3LL, 0x21842115a5423421LL, 0x9c4a9c94d625089cLL, 0x1e781ef0663cee1eLL,
    0x4311432252866143LL, 0xc73bc776fc93b1c7LL, 0xfcd7fcb32be54ffcLL, 0x0410042014082404LL,
    0x515951b208a2e351LL, 0x995e99bcc72f2599LL, 0x6da96d4fc4da226dLL, 0x0d340d68391a650dLL,
    0xfacffa8335e979faLL, 0xdf5bdfb684a369dfLL, 0x7ee57ed79bfca97eLL, 0x2490243db4481924LL,
    0x3bec3bc5d776fe3bLL, 0xab96ab313d4b9aabLL, 0xce1fce3ed181f0ceLL, 0x1144118855229911LL,
    0x8f068f0c8903838fLL, 0x4e254e4a6b9c044eLL, 0xb7e6b7d1517366b7LL, 0xeb8beb0b60cbe0ebLL,
    0x3cf03cfdcc78c13cLL, 0x813e817cbf1ffd81LL, 0x946a94d4fe354094LL, 0xf7fbf7eb0cf31cf7LL,
    0xb9deb9a1676f18b9LL, 0x134c13985f268b13LL, 0x2cb02c7d9c58512cLL, 0xd36bd3d6b8bb05d3LL,
    0xe7bbe76b5cd38ce7LL, 0x6ea56e57cbdc396eLL, 0xc437c46ef395aac4LL, 0x030c03180f061b03LL,
    0x5645568a13acdc56LL, 0x440d441a49885e44LL, 0x7fe17fdf9efea07fLL, 0xa99ea921374f88a9LL,
    0x2aa82a4d8254672aLL, 0xbbd6bbb16d6b0abbLL, 0xc123c146e29f87c1LL, 0x535153a202a6f153LL,
    0xdc57dcae8ba572dcLL, 0x0b2c0b582716530bLL, 0x9d4e9d9cd327019dLL, 0x6cad6c47c1d82b6cLL,
    0x31c43195f562a431LL, 0x74cd7487b9e8f374LL, 0xf6fff6e309f115f6LL, 0x4605460a438c4c46LL,
    0xac8aac092645a5acLL, 0x891e893c970fb589LL, 0x145014a04428b414LL, 0xe1a3e15b42dfbae1LL,
    0x165816b04e2ca616LL, 0x3ae83acdd274f73aLL, 0x69b9696fd0d20669LL, 0x092409482d124109LL,
    0x70dd70a7ade0d770LL, 0xb6e2b6d954716fb6LL, 0xd067d0ceb7bd1ed0LL, 0xed93ed3b7ec7d6edLL,
    0xcc17cc2edb85e2ccLL, 0x4215422a57846842LL, 0x985a98b4c22d2c98LL, 0xa4aaa4490e55eda4LL,
    0x28a0285d88507528LL, 0x5c6d5cda31b8865cLL, 0xf8c7f8933fed6bf8LL, 0x86228644a411c286LL,
};

static const uint64_t rc[ROUNDS] = {
    0x1823c6e887b8014fLL, 0x36a6d2f5796f9152LL,
    0x60bc9b8ea30c7b35LL, 0x1de0d7c22e4bfe57LL,
    0x157737e59ff04adaLL, 0x58c9290ab1a06b85LL,
    0xbd5d10f4cb3e0567LL, 0xe427418ba77d95d8LL,
    0xfbee7c66dd17479eLL, 0xca2dbf07ad5a8333LL
};

/* The core Whirlpool transform */
static void
process_buffer(uint64_t hash[DIGESTBYTES/8], uint8_t buf[WBLOCKBYTES])
{
	uint64_t	K[8];        /* the round key */
	uint64_t	block[8];    /* mu(buffer) */
	uint64_t	state[8];    /* the cipher state */
	uint64_t	L[8];

#ifdef TRACE_INTERMEDIATE_VALUES
	{
		uint8_t		*buf_ = ctx->buf;
		printf("The 8x8 matrix Z' derived from the data-string is "
		    "as follows.\n");
		for (uint8_t i = 0; i < WBLOCKBYTES/8; i++)
			printf("    %02hhX %02hhX %02hhX %02hhX "
			    "%02hhX %02hhX %02hhX %02hhX\n",
			    buf_[0], buf_[1], buf_[2], buf_[3],
			    buf_[4], buf_[5], buf_[6], buf_[7]);
			buf_ += 8;
		printf("\n");
	}
#endif /* ?TRACE_INTERMEDIATE_VALUES */

	/* map the buffer to a block */
	be_to_host64(block, buf, WBLOCKBYTES);

	/* compute and apply K_0 to the cipher state */
	state[0] = block[0] ^ (K[0] = hash[0]);
	state[1] = block[1] ^ (K[1] = hash[1]);
	state[2] = block[2] ^ (K[2] = hash[2]);
	state[3] = block[3] ^ (K[3] = hash[3]);
	state[4] = block[4] ^ (K[4] = hash[4]);
	state[5] = block[5] ^ (K[5] = hash[5]);
	state[6] = block[6] ^ (K[6] = hash[6]);
	state[7] = block[7] ^ (K[7] = hash[7]);

#ifdef TRACE_INTERMEDIATE_VALUES
	printf("The K_0 matrix (from the initialization value IV) and "
	    "X'' matrix are as follows.\n");
	for (uint8_t i = 0; i < DIGESTBYTES/8; i++)
		printf("    %02hhX %02hhX %02hhX %02hhX "
		    "%02hhX %02hhX %02hhX %02hhX        "
		    "%02hhX %02hhX %02hhX %02hhX "
		    "%02hhX %02hhX %02hhX %02hhX\n",
		    (uint8_t)(K[i] >> 56),
		    (uint8_t)(K[i] >> 48),
		    (uint8_t)(K[i] >> 40),
		    (uint8_t)(K[i] >> 32),
		    (uint8_t)(K[i] >> 24),
		    (uint8_t)(K[i] >> 16),
		    (uint8_t)(K[i] >>  8),
		    (uint8_t)(K[i]      ),
		    (uint8_t)(state[i] >> 56),
		    (uint8_t)(state[i] >> 48),
		    (uint8_t)(state[i] >> 40),
		    (uint8_t)(state[i] >> 32),
		    (uint8_t)(state[i] >> 24),
		    (uint8_t)(state[i] >> 16),
		    (uint8_t)(state[i] >>  8),
		    (uint8_t)(state[i]      ));
	printf("\n");
	printf("The following are (hexadecimal representations of) the "
	    "successive values of the variables K_i for i = 1 to 10 and "
	    "W'.\n\n");
#endif /* ?TRACE_INTERMEDIATE_VALUES */

	/* iterate over all rounds */
	for (uint8_t r = 0; r < ROUNDS; r++) {
		/* compute K_r from K_{r-1} */
		L[0] =
		    c_0[         (K[0] >> 56)] ^
		    c_1[(uint8_t)(K[7] >> 48)] ^
		    c_2[(uint8_t)(K[6] >> 40)] ^
		    c_3[(uint8_t)(K[5] >> 32)] ^
		    c_4[(uint8_t)(K[4] >> 24)] ^
		    c_5[(uint8_t)(K[3] >> 16)] ^
		    c_6[(uint8_t)(K[2] >>  8)] ^
		    c_7[(uint8_t)(K[1]      )] ^
		    rc[r];
		L[1] =
		    c_0[         (K[1] >> 56)] ^
		    c_1[(uint8_t)(K[0] >> 48)] ^
		    c_2[(uint8_t)(K[7] >> 40)] ^
		    c_3[(uint8_t)(K[6] >> 32)] ^
		    c_4[(uint8_t)(K[5] >> 24)] ^
		    c_5[(uint8_t)(K[4] >> 16)] ^
		    c_6[(uint8_t)(K[3] >>  8)] ^
		    c_7[(uint8_t)(K[2]      )];
		L[2] =
		    c_0[         (K[2] >> 56)] ^
		    c_1[(uint8_t)(K[1] >> 48)] ^
		    c_2[(uint8_t)(K[0] >> 40)] ^
		    c_3[(uint8_t)(K[7] >> 32)] ^
		    c_4[(uint8_t)(K[6] >> 24)] ^
		    c_5[(uint8_t)(K[5] >> 16)] ^
		    c_6[(uint8_t)(K[4] >>  8)] ^
		    c_7[(uint8_t)(K[3]      )];
		L[3] =
		    c_0[         (K[3] >> 56)] ^
		    c_1[(uint8_t)(K[2] >> 48)] ^
		    c_2[(uint8_t)(K[1] >> 40)] ^
		    c_3[(uint8_t)(K[0] >> 32)] ^
		    c_4[(uint8_t)(K[7] >> 24)] ^
		    c_5[(uint8_t)(K[6] >> 16)] ^
		    c_6[(uint8_t)(K[5] >>  8)] ^
		    c_7[(uint8_t)(K[4]      )];
		L[4] =
		    c_0[         (K[4] >> 56)] ^
		    c_1[(uint8_t)(K[3] >> 48)] ^
		    c_2[(uint8_t)(K[2] >> 40)] ^
		    c_3[(uint8_t)(K[1] >> 32)] ^
		    c_4[(uint8_t)(K[0] >> 24)] ^
		    c_5[(uint8_t)(K[7] >> 16)] ^
		    c_6[(uint8_t)(K[6] >>  8)] ^
		    c_7[(uint8_t)(K[5]      )];
		L[5] =
		    c_0[         (K[5] >> 56)] ^
		    c_1[(uint8_t)(K[4] >> 48)] ^
		    c_2[(uint8_t)(K[3] >> 40)] ^
		    c_3[(uint8_t)(K[2] >> 32)] ^
		    c_4[(uint8_t)(K[1] >> 24)] ^
		    c_5[(uint8_t)(K[0] >> 16)] ^
		    c_6[(uint8_t)(K[7] >>  8)] ^
		    c_7[(uint8_t)(K[6]      )];
		L[6] =
		    c_0[         (K[6] >> 56)] ^
		    c_1[(uint8_t)(K[5] >> 48)] ^
		    c_2[(uint8_t)(K[4] >> 40)] ^
		    c_3[(uint8_t)(K[3] >> 32)] ^
		    c_4[(uint8_t)(K[2] >> 24)] ^
		    c_5[(uint8_t)(K[1] >> 16)] ^
		    c_6[(uint8_t)(K[0] >>  8)] ^
		    c_7[(uint8_t)(K[7]      )];
		L[7] =
		    c_0[         (K[7] >> 56)] ^
		    c_1[(uint8_t)(K[6] >> 48)] ^
		    c_2[(uint8_t)(K[5] >> 40)] ^
		    c_3[(uint8_t)(K[4] >> 32)] ^
		    c_4[(uint8_t)(K[3] >> 24)] ^
		    c_5[(uint8_t)(K[2] >> 16)] ^
		    c_6[(uint8_t)(K[1] >>  8)] ^
		    c_7[(uint8_t)(K[0]      )];
		K[0] = L[0];
		K[1] = L[1];
		K[2] = L[2];
		K[3] = L[3];
		K[4] = L[4];
		K[5] = L[5];
		K[6] = L[6];
		K[7] = L[7];
		/* apply the r-th round transformation */
		L[0] ^=
		    c_0[         (state[0] >> 56)] ^
		    c_1[(uint8_t)(state[7] >> 48)] ^
		    c_2[(uint8_t)(state[6] >> 40)] ^
		    c_3[(uint8_t)(state[5] >> 32)] ^
		    c_4[(uint8_t)(state[4] >> 24)] ^
		    c_5[(uint8_t)(state[3] >> 16)] ^
		    c_6[(uint8_t)(state[2] >>  8)] ^
		    c_7[(uint8_t)(state[1]      )];
		L[1] ^=
		    c_0[         (state[1] >> 56)] ^
		    c_1[(uint8_t)(state[0] >> 48)] ^
		    c_2[(uint8_t)(state[7] >> 40)] ^
		    c_3[(uint8_t)(state[6] >> 32)] ^
		    c_4[(uint8_t)(state[5] >> 24)] ^
		    c_5[(uint8_t)(state[4] >> 16)] ^
		    c_6[(uint8_t)(state[3] >>  8)] ^
		    c_7[(uint8_t)(state[2]      )];
		L[2] ^=
		    c_0[         (state[2] >> 56)] ^
		    c_1[(uint8_t)(state[1] >> 48)] ^
		    c_2[(uint8_t)(state[0] >> 40)] ^
		    c_3[(uint8_t)(state[7] >> 32)] ^
		    c_4[(uint8_t)(state[6] >> 24)] ^
		    c_5[(uint8_t)(state[5] >> 16)] ^
		    c_6[(uint8_t)(state[4] >>  8)] ^
		    c_7[(uint8_t)(state[3]      )];
		L[3] ^=
		    c_0[         (state[3] >> 56)] ^
		    c_1[(uint8_t)(state[2] >> 48)] ^
		    c_2[(uint8_t)(state[1] >> 40)] ^
		    c_3[(uint8_t)(state[0] >> 32)] ^
		    c_4[(uint8_t)(state[7] >> 24)] ^
		    c_5[(uint8_t)(state[6] >> 16)] ^
		    c_6[(uint8_t)(state[5] >>  8)] ^
		    c_7[(uint8_t)(state[4]      )];
		L[4] ^=
		    c_0[         (state[4] >> 56)] ^
		    c_1[(uint8_t)(state[3] >> 48)] ^
		    c_2[(uint8_t)(state[2] >> 40)] ^
		    c_3[(uint8_t)(state[1] >> 32)] ^
		    c_4[(uint8_t)(state[0] >> 24)] ^
		    c_5[(uint8_t)(state[7] >> 16)] ^
		    c_6[(uint8_t)(state[6] >>  8)] ^
		    c_7[(uint8_t)(state[5]      )];
		L[5] ^=
		    c_0[         (state[5] >> 56)] ^
		    c_1[(uint8_t)(state[4] >> 48)] ^
		    c_2[(uint8_t)(state[3] >> 40)] ^
		    c_3[(uint8_t)(state[2] >> 32)] ^
		    c_4[(uint8_t)(state[1] >> 24)] ^
		    c_5[(uint8_t)(state[0] >> 16)] ^
		    c_6[(uint8_t)(state[7] >>  8)] ^
		    c_7[(uint8_t)(state[6]      )];
		L[6] ^=
		    c_0[         (state[6] >> 56)] ^
		    c_1[(uint8_t)(state[5] >> 48)] ^
		    c_2[(uint8_t)(state[4] >> 40)] ^
		    c_3[(uint8_t)(state[3] >> 32)] ^
		    c_4[(uint8_t)(state[2] >> 24)] ^
		    c_5[(uint8_t)(state[1] >> 16)] ^
		    c_6[(uint8_t)(state[0] >>  8)] ^
		    c_7[(uint8_t)(state[7]      )];
		L[7] ^=
		    c_0[         (state[7] >> 56)] ^
		    c_1[(uint8_t)(state[6] >> 48)] ^
		    c_2[(uint8_t)(state[5] >> 40)] ^
		    c_3[(uint8_t)(state[4] >> 32)] ^
		    c_4[(uint8_t)(state[3] >> 24)] ^
		    c_5[(uint8_t)(state[2] >> 16)] ^
		    c_6[(uint8_t)(state[1] >>  8)] ^
		    c_7[(uint8_t)(state[0]      )];
		state[0] = L[0];
		state[1] = L[1];
		state[2] = L[2];
		state[3] = L[3];
		state[4] = L[4];
		state[5] = L[5];
		state[6] = L[6];
		state[7] = L[7];

#ifdef TRACE_INTERMEDIATE_VALUES
		printf("i = %d:\n", r);
		for (uint8_t i = 0; i < DIGESTBYTES/8; i++)
		    printf("    %02hhX %02hhX %02hhX %02hhX "
			"%02hhX %02hhX %02hhX %02hhX        "
			"%02hhX %02hhX %02hhX %02hhX "
			"%02hhX %02hhX %02hhX %02hhX\n",
			    (uint8_t)(K[i] >> 56),
			    (uint8_t)(K[i] >> 48),
			    (uint8_t)(K[i] >> 40),
			    (uint8_t)(K[i] >> 32),
			    (uint8_t)(K[i] >> 24),
			    (uint8_t)(K[i] >> 16),
			    (uint8_t)(K[i] >>  8),
			    (uint8_t)(K[i]      ),
			    (uint8_t)(state[i] >> 56),
			    (uint8_t)(state[i] >> 48),
			    (uint8_t)(state[i] >> 40),
			    (uint8_t)(state[i] >> 32),
			    (uint8_t)(state[i] >> 24),
			    (uint8_t)(state[i] >> 16),
			    (uint8_t)(state[i] >>  8),
			    (uint8_t)(state[i]      ));
		printf("\n");
#endif /* ?TRACE_INTERMEDIATE_VALUES */

	}
	/* apply the Miyaguchi-Preneel compression function */
	hash[0] ^= state[0] ^ block[0];
	hash[1] ^= state[1] ^ block[1];
	hash[2] ^= state[2] ^ block[2];
	hash[3] ^= state[3] ^ block[3];
	hash[4] ^= state[4] ^ block[4];
	hash[5] ^= state[5] ^ block[5];
	hash[6] ^= state[6] ^ block[6];
	hash[7] ^= state[7] ^ block[7];

#ifdef TRACE_INTERMEDIATE_VALUES
	//printf("Intermediate hash value (after Miyaguchi-Preneel):\n");
	printf("The value of Y' output from the round-function is "
	    "as follows.\n");
	for (uint8_t i = 0; i < DIGESTBYTES/8; i++)
		printf("    %02hhX %02hhX %02hhX %02hhX "
		    "%02hhX %02hhX %02hhX %02hhX\n",
		    (uint8_t)(hash[i] >> 56),
		    (uint8_t)(hash[i] >> 48),
		    (uint8_t)(hash[i] >> 40),
		    (uint8_t)(hash[i] >> 32),
		    (uint8_t)(hash[i] >> 24),
		    (uint8_t)(hash[i] >> 16),
		    (uint8_t)(hash[i] >>  8),
		    (uint8_t)(hash[i]      ));
	printf("\n");
#endif /* ?TRACE_INTERMEDIATE_VALUES */
}

/* Initialize the hashing state */
void
whirlpool_init(struct whirlpool_ctx *ctx)
{
	memset(ctx->hash, 0, sizeof(ctx->hash));
	memset(ctx->bit_count, 0, sizeof(ctx->bit_count));
	ctx->pos = 0;
}

static inline void
increment_count(struct whirlpool_ctx *ctx, size_t sz)
{
	uint64_t value = (uint64_t)sz * 8;
	uint64_t sum;
	for (int8_t i = 7; i >= 0 && value; i--) {
		sum = (uint32_t)value + ctx->bit_count[i];
		ctx->bit_count[i] = (uint32_t)sum;
		value = (value + sum) >> 32;
	}
	assert(!value);
}

/* Delivers input data to the hashing algorithm.
 * This method maintains the invariant: pos < WBLOCKBYTES */
void
whirlpool_update(struct whirlpool_ctx *ctx, const uint8_t *buf, size_t sz)
{
	/* tally the length of the added data */
	increment_count(ctx, sz);

	if (ctx->pos + sz < WBLOCKBYTES) {
		/* buffer will not fill */
		memcpy(ctx->buf + ctx->pos, buf, sz);
		ctx->pos += sz;
		return;
	}

	/* do first (full) block */
	uint8_t bytes = WBLOCKBYTES - ctx->pos;
	memcpy(ctx->buf + ctx->pos, buf, bytes);
	process_buffer(ctx->hash, ctx->buf);
	buf += bytes;
	sz -= bytes;

	/* do subsequent (full) blocks */
	while (sz >= WBLOCKBYTES) {
		memcpy(ctx->buf, buf, WBLOCKBYTES);
		process_buffer(ctx->hash, ctx->buf);
		buf += WBLOCKBYTES;
		sz -= WBLOCKBYTES;
	}

	/* copy next partial block for later */
	if (sz)
		memcpy(ctx->buf, buf, sz);
	ctx->pos = sz;
}

/* append bit length of hashed data */
static inline void
append_bit_count(struct whirlpool_ctx *ctx)
{
	uint8_t		buf[LENGTHBYTES];
	host_to_be32(buf, ctx->bit_count, LENGTHBYTES);
	memcpy(ctx->buf + ctx->pos, buf, LENGTHBYTES);
}

/* Get the hash value from the hashing state.
 * This method uses the invariant: pos < WBLOCKBYTES */
void
whirlpool_end(struct whirlpool_ctx *ctx, uint8_t *buf, size_t sz_buf)
{
	/* append a '1'-bit */
	ctx->buf[ctx->pos++] = 0x80;

	/* pad with zero bits to complete (N*WBLOCKBYTES - LENGTHBYTES) bits */

	if (ctx->pos + LENGTHBYTES > WBLOCKBYTES) {
		/* no room to fit the bit count; that will go in the next
		 * block */

		if (ctx->pos != WBLOCKBYTES)
			/* fill remainder with zeros */
			memset(ctx->buf + ctx->pos, 0,
			    WBLOCKBYTES - ctx->pos);

		process_buffer(ctx->hash, ctx->buf);
		ctx->pos = 0;
	}
	if (ctx->pos + LENGTHBYTES != WBLOCKBYTES) {
		/* pad middle with zeros */
		memset(ctx->buf + ctx->pos, 0,
		    WBLOCKBYTES - (ctx->pos + LENGTHBYTES));
		ctx->pos = WBLOCKBYTES - LENGTHBYTES;
	}

	append_bit_count(ctx);

	/* process final data block */
	process_buffer(ctx->hash, ctx->buf);

	/* return digest */
	if (sz_buf > DIGESTBYTES) sz_buf = DIGESTBYTES;
	host_to_be64(buf, ctx->hash, sz_buf);
}
